简体   繁体   中英

Is a std::vector pointer reliable?

Say I have this:

Object *myObject;
std::vector<Object> objects(99);

....
myObject = &objects[4];

Would it be safe to assume that myObject's pointer will always be valid no matter how big objects[] gets and if I remove some, as long as I do not ever erase() the actual object pointed to by myObject? Or does it work differently? If the above does not work, how else could I achieve this? It's because in my design, I want a child to have a pointer to its parent, which is assigned by the parent when the child is added, however the parents are in a std::vector, I need random access. Would a std::list be better in my case?

Thanks

No, it is definitely not safe to assume that.

The standard explains what will/won't invalidate iterators on standard containers; any time a vector iterator is invalidated, a pointer into it would be too. In practice this means that when the vector resizes (including implicitly when you call push_back() ), any iterators pointing into it will be invalidated, as would your pointer. Similarly, calling erase() will invalidate pointers after the erased item because they all have to move up to fill the erased space.

A std::list would work better; you could maintain in parallel a vector of pointers to the items which would allow you to access them by index without them moving about in memory.

If the vector fills up its allocated space and you try to insert more, it needs to re-allocate its storage, and that will involve copying over the objects and destroying the old storage, which would invalidate the pointer you're keeping around - so no, this is not good enough.

std::list would work fine, since it doesn't rely on continuous storage, but you lose the ability to do quick random access. Alternatively, you can store pointers to your Objects in your collection, in which case you can just take out that element, and the pointer will remain valid until you free that memory in your code - but you will need to handle that yourself at some point.

Another option might be a deque ; while deque iterators are invalidated by push_back , direct references to elements (like the pointer you're using here) remain valid.

Provided you keep the vector the same size, you can guarantee this by calling reserve() with the maximum number of elements after declaring it or (as you have done) declaring it with an initial number of elements each constructed to have the default element value.

If you remove or add elements, the underlying storage can get reallocated at any time.

Since you are using raw pointers here, you could use NULL as an "empty element" flag to keep the storage invariant. Since you are setting it up with 99 initially, they will all be NULL after that (the default value for any pointer as a vector element) and reserve is redundant unless you plan to extend the list.

An option that would allow you to not worry about the vector storage would be to store the elements as boost::shared_ptr<Object> . Then any removal of the vector element will only actually delete the referenced Object instance if nobody else is using it.

boost::shared_ptr<Object> myObject;
std::vector<boost::shared_ptr<Object> > objects(99);

myObject = &objects[4];

objects.clear();
// myObject still valid since Object instance referenced thru shared_ptr

Strange, but no one mentioned the boost::stable_vector thing:

...references and iterators to an element of a stable_vector remain valid as long as the element is not erased, and an iterator that has been assigned the return value of end() always remain valid until the destruction of the associated stable_vector.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM