Interface to access elements without breaking encapsulation

Hi

I am designing a library which will store data supplied by the user. I would like for the user to be able to change or delete these objects. But I am conscious that it would be bad design to directly expose the data to the user (as I think that would be breaking the encapsulation).

I am really struggling to do this. I think an example will explain the problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Container
{

   private:
     multimap<unsigned int, char> mData; // multimap so can be multiple identical keys with different values  

   public:
     void addData(unsigned int ID, char data)
     {
         mData.insert(pair<unsigned int, char>(ID, data)); 
     }
     
};


So in this Container there could be multiple identical keys with different values stored in a multimap. What I would like for the user to do is, for example, change the value associated with one key (but only that value and not any other value with the same key).

What I am struggling with is how to allow the user to do this given that there can be multiple values associated with the same key. I could create a method to return all values with a given key but how can the user then delete one of these values?

Thanks
Maybe the user can supply key and value.
change the value associated with one key


How is the user to know which key to change? std::equal_range returns the bounds of a range that includes all the elements in the container which have a key equal to that specified. If the user then chooses which one to delete (display the data values of each iterator in the range), then you have an iterator to the one required which can then be used to erase the one required.
Consider as an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <map>
#include <iostream>

bool display(const std::multimap<int, int>& mm) {
	for (const auto& [k, v] : mm)
		std::cout << '(' << k << ',' << v << ") ";

	return !!(std::cout << "\n\n");
}

int main() {
	std::multimap<int, int> mm { {1, 1}, {1, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3} };

	for (bool again { display(mm)}; again; ) {
		int key {};

		std::cout << "Enter key to erase: ";
		std::cin >> key;

		const auto bounds { mm.equal_range(key) };

		if (bounds.first == mm.end())
			std::cout << "Key not found\n";
		else
			if (std::distance(bounds.first, bounds.second) == 1)
				mm.erase(bounds.first);
			else {
				std::cout << "There are multiple keys\n";

				size_t elem {}, val {};

				for (auto itr(bounds.first); itr != bounds.second; ++itr, ++elem)
					std::cout << elem + 1 << ". " << itr->second << '\n';

				std::cout << "Which one to erase: ";
				std::cin >> val;

				if (val == 0 || val > elem)
					std::cout << "Invalid value\n";
				else
					mm.erase(std::next(bounds.first, val - 1));
			}

		display(mm);

		char res {};

		std::cout << "Erase another (y/n) :";
		std::cin >> res;

		again = res == 'Y' || res == 'y';
	}
}

Great thanks.

I wasn't very clear when I said "user". I meant a client using the library - what methods should I make available to allow users to change mapped values?
If I were providing an API to your Container class, I would provide the following at a minimum:
- Insert key,val pair
- Delete key,val pair
- Find key,val pair
- Return iterator to first key,val pair - begin()
- Return iterator to last+1 key,val pair - end()
- Provide an override for the << operator to format a key,val pair.
That should provide the basic functions to manipulate your Container class.
From there, I would look at the function calls provided by std::multimap.
Since your multimap is private, you will want to provide a public iterator for your class.
// container.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma once
#include <map>
#include <iostream>

class Container
{
public:
    typedef int key_type;
    typedef char val_type;
    typedef std::multimap<key_type, val_type>   container_t;
    typedef container_t::iterator               iterator;

private:
    container_t     m_map; // multimap so can be multiple identical keys with different values  

public:
    //  Returns an interator pointing to the newly inserted element
    iterator insert(key_type key, val_type val);
    //  Returns an iterator to the first element with the specified key or end()
    iterator find(key_type key);
    //  Removes all elements with specified key.  Returns number of elements removed.
    size_t erase(key_type key);
    //  Return iterator to first element in the multimap
    iterator begin();
    //  Return iterator to last+1 element in the multimap
    iterator end();    
};

// container.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <map>
#include "container.h"

using namespace std;

Container::iterator Container::insert(Container::key_type key, Container::val_type val)
{
	pair<Container::key_type, Container::val_type>		pr(key, val);
	return m_map.insert(pr); 
}

Container::iterator Container::find(Container::key_type key)
{
	return m_map.find(key);
}

size_t Container::erase(Container::key_type key)
{
	return m_map.erase(key);
}

Container::iterator Container::begin()
{
	return m_map.begin();
}

Container::iterator Container::end()
{
	return m_map.end();
}
thanks everyone, very helpful
Topic archived. No new replies allowed.