Let’s assume there are 2 classes: Country and City. Both of them have names, so a map would suit as conainer
map<std::string, Country> countries;
In the country class there is a similar map that contains all cities – for the sake of simplicity, let’s say it’s public:
map<std::string, City> cities;
Now on top level if a specific city should be addressed, the request looks like this:
countries.at("Japan").cities.at("Tokyo");
(In the real world I should rather use find
and check every time if the desired country/city does actually exist, right?)
Now let’s say at top level we want to store in a vector a set of our favourite cities. I see 2 options to do so:
// Option 1: Store the cities as references:
vector<City&> favouriteCities;
// Option 2: Store the ID's (Names) of the countries and the cities as strings
vector<pair<std::string,std::string>> favouriteCities;
So to get my first favourite city in option 2 I’ll have to access it like this:
countries.at(favouriteCities[0].first).cities.at(favouriteCities[0].second);
What would be the pros and cons of these options? AFAIK: Option 1 is more performant and more convenient. But with option 2 you have te chance to check if the desired favourite city is still in the system and has not been deleted in the meantime (so a reference would be invalid).
Are there more points to consider?
1
Option 2 is definitely best.
The main con of option 1 is that it is impossible; standard containers may not contain references.
Though you could “fake” it using std::ref
, storing handles to container elements is never a good idea anyway. What happens when those elements are invalidated by map erasure? At worst you want a std::unique_ptr
, and at best you want to make use of the convenient string keys you already use.
If you discover (through profiling!) that the string comparisons are a bottleneck, only then consider implementing something a more complex, more “algorithmic” solution.
4
Instead of a simple reference, you could store a shared_ptr of the City or Country, then it can be removed from the map and still exist in memory (ie while it is still favourited). It’ll only be deleted from memory when all references are removed.
Alternatively, you can store the City and Country entries in a container (eg a vector so you gain from a contiguous block of storage) specifically to hold them, and have both favourites and map point to these initial entries – a bit like a very high-level “malloc and pointer” approach. The concept is that you have 1 entity that holds ownership of the objects, and everything else can then simply refer or point to them without any worries over ownership and object lifetimes.