开发者

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 the end iterator is returned
  • operator[] 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜