Is it safe to get an object in std::map by reference?
I have a map like this
map<int,object> objmap;
object& obj = objmap.find(num)->second;
object& obj2 = objmap[num];
Whatever changes I make in the object have to be reflected on the map. Sim开发者_开发问答ilar thing cant be done in a vector as it changes location of objects when it wants more space. Is it safe to do it in a std::map? and is it advisable? The second version gives an error as my object doesn't have an empty constructor. If I declare an empty constructor doing nothing, will the two lines will work the same way?
So long as the object in question isn't removed from the map, then yes it is safe. Once inserted into a map objects don't move around even if other elements are added or removed.
object& obj = objmap.find(num)->second;
This is potentially dangerous unless you are sure that an element with key num
actually exists in the map. If you are not sure, you could use the overload of insert
that returns an iterator
and a bool
which indicates whether a new element was inserted or an element with the given key was already present in the map.
E.g.
object& obj = objmap.insert( std::make_pair(num, object(arg1, arg2, argN)) ).first->second;
This is safe as long as the element isn't removed from the map.
However, the second line is not quite safe :
object& obj = objmap.find(num)->second;
If there is no elements with key num
in the map, find
will return objmap.end()
. This possibility should be tested before dereferencing the returned iterator :
const std::map<int, object>::iterator it = objmap.find(num);
if (it != objmap.end())
{
object& obj = it->second;
/* ... */
}
Now, if the goal isn't really to find but really to insert, calling operator[]
is a possibility (although, as you already noticed, it requires the value to provide a parameterless constructor). But you have to understand that these are two very different things :
find
only finds : if the key isn't found, nothing gets inserted and theend
iterator is returnedoperator[]
always returns a reference to a value in the map : were the key absent, an insertion occurs (for a default constructed value : thus the constructor requirement)
If your question is whether the std::map
invalidates its iterators in its mutating functions then the answer is negative. The standard guarantees std::map
doesn't invalidate its iterators.
If you want changes made to the object in the map to be reflected, you will likely want to change your map to store pointers to the objects instead of objects themselves. The references can work as long as you do not do anything to the object in between the reference that would invalidate it.
For example, the following code would break using references:
object& obj = objmap.find(num)->second;
objmap.erase(objmap.find(num)); // should check for objmap.end() - left out for simplicity
obj.DoSomething(); // this object has been destroyed, so the reference is invalid
Doing what you are doing is a common way to implement caching.
If the item is there already then operator[] returns the item. If it is not there it will create a "blank" with the default constructor and you can write into it for later use.
Of course one commonly uses shared_ptr as the value_type which will have an "empty" value when created. In this case you need to get the shared_ptr by reference so you can call reset() on it.
As with any collections / caching etc. you must beware of thread-safety issues if it is being done in a multi-threaded application.
What I am not sure you wanted to know was whether you could store the reference somewhere (or a pointer to it) and expect it to be valid later when other items are added to the map, and yes, it will be.
精彩评论