Is a std::vector pointer reliable? - c++

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.

Related

Is it safe to create a vector with new?

When a vector becomes full it is reallocated and all of it iterators and pointers to elements are invalidated. Does this behaviour change in any way when the vector is created with new?
std::vector<int>* v = new std::vector<int>(); //What happens if this vector gets reallocated?
And can I get the object from its pointer (as a separate variable), or do I have to copy it to another object?
As long as v is not destroyed, the underlying array (wrapped inside by the std::vector class template ) is in good state, irrespective of any reallocation.
To answer the title question: yes, it's safe (if unusual) to allocate a std::vector object dynamically.
To answer the body question:
How the std::vector object itself is allocated does not affect the validity of its iterators etc. at all. They still become invalidated by reallocation of the std::vector's internal data store.
For the final question, I don't quite understand. *v will give you a reference to the dynamically allocated vector. It does not copy anything. (*v)[3] will give you the int on index 3 in the vector, for example.
It doesn't matter whether you create the vector on the stack or on the heap. The vector actually owns the storage space which is probably just an array that it allocates and reallocates. The pointer you have is to the owner which doesn't change. The only difference with newing it is that you are responsible for releasing the owner (presumably the owner releases the owned memory).
Using new is safe, but doesn't help the problem you have with dynamic reallocation. The vector object promises continuity of the storage space, so there is not much you can do. After certain operations, you may not rely on iterators anymore. To prevent dynamic reallocation at unknown times use reserve() or resize() explicitly.

vector of pointer

I just wonder what's wrong with vector of pointer. some of my friend recommend me to use list instead of vector. will this cause a problem:
vector<Fruit*> basket;
basket.push_back(new Apple());
basket.push_back(new Orange());
vector<Fruit*> temp;
temp.push_back(basket[1]);
If I delete vector temp, do I destroy the basket[1] object too? if not, what's the problem with using vector of pointer?
If I delete vector temp, do I destroy the basket[1] object too?
No. First of all, you cannot delete temp; rather, it will get destroyed when going out of scope. And when this happens, the objects pointed by elements of the vector won't be automatically deleted.
This is, however, not a specific problem of vector: using list will not save you from this issue. The problem is rather with raw pointers. If you want the pointed objects to be automatically deallocated when the the lifetime of the last pointer which points to it ends, you should use smart pointers.
Depending on the ownership policy that your application needs, you might choose between shared_ptr and unique_ptr. The caveat with shared_ptr is that referencing cycles shall be avoided, to prevent mutually referencing objects from keeping each other alive. You may want to check weak_ptr in this respect.
Finally, unless you have a good reason for not using vector, vector should be the default choice for a container. From Paragraph 23.2.3/2 of the C++11 Standard:
The sequence containers offer the programmer different complexity trade-offs and should be used accordingly. vector or array is the type of sequence container that should be used by default. list or forward_list should be used when there are frequent insertions and deletions from the middle of the sequence. deque is the data structure of choice when most insertions and deletions take place at the beginning or at the end of the sequence.
If either of the vectors, basket or temp, are destroyed none of the Fruits are destroyed. Anything that is dynamically allocated with new must be deleted. When the vectors go out of scope, they do not delete the objects that are pointed to by their elements. If you delete an object through one vector that is pointed to in the other vector, both point at the now deleted object.
Since your vectors have automatic storage duration, you definitely must not do delete basket; or delete temp;.
It's not recommended to use raw pointers in general, especially with dynamically allocated storage that you may forget to delete. In this case, however, you do need polymorphic behaviour, which is provided by pointers. You should use a smart pointer to avoid the problems with raw pointers. Try a std::vector<std::shared_ptr<Fruit>>.

Why do I need to delete pointers from vector manually?

Why do I need to delete dynamically created items in vector, manually? Why wouldn't they get deleted or its destructor called when vector got deleted?
Normally something like this, but why needed ?
vector<int*> v;
for (vector<int*>::iterator it = v.begin(); it != v.end(); ++it)
{
delete *it;
}
Firstly, you store raw pointers in your vector. These pointers are just pointers. They can point anywhere. They can point to local objects, which cannot be deleted by delete. And even if they point to dynamically created objects, it doesn't necessarily mean that the user wants them to die with the vector. How is the vector supposed to know all this?
It is a matter of object ownership. Whoever owns the object is responsible for its proper and timely deletion. Ordinary raw pointers do not express ownership. That is why vector can't make any assumptions about whether the objects need to be deleted or not. If you want to tell the vector that it owns its dynamic objects, use corresponding smart pointers.
Secondly, note that your deletion technique is not necessarily safe in the general case. Some standard containers assume that the data you store in them is always valid. When you do delete on each vector element, the data in the vector becomes "invalidated" (pointers become indeterminate). This is OK with a vector. But doing something like this in a "smarter" container, like std::map or std::unordered_set for example, can and will lead to problems. Even if you destroy the container itself immediately afterwards, it is perfectly possible that the container's destruction algorithm might need to analyze (compare, hash etc.) the values of individual elements. And you just killed them all with your cycle.
Smart pointers naturally resolve this matter. But if you have to use a manual delete for raw pointers stored in a standard container, a better sequence of steps would be this
Retrieve the value of the element at i and store it in pointer p
Erase the element at i from the container
Do delete p
In the end you end up with an empty container and deleted data. Again, your approach will work for simple sequences, like std::vector or std::list, but don't do this with ordered or hashed ones.
Because the language has no way of knowing whether you need them to remain alive when you're done. What if you inserted some pointers to stack objects or something like that? Boom.
This, however, can be remedied by using a smart pointer, which will implement an appropriate policy as to automatic destruction. unique_ptr and shared_ptr are the most common and useful.
The destructor is not called because the pointer does not own the object to which it points.
In C++11, a std::unique_ptr<T> does indeed own the object to which it points. In this case, the destructor is called when the pointer is released, which is probably the behavior you want. (If you don't have a C++11 compiler but have a reasonably recent pre-C++11 compiler, unique_ptr is still available as part of TR1.)
The implementation of vector was just made to delete its memory only. Pointers do not have deconstructors. And in C++, there is no garbage collection or logic to see that it is pointers and recursive delete it.
If you would like to attend the next ISO meeting and propose that, be my guest, but for me, I just put it in an inline function:
template<class T>
public static inline void deleteVectorPointer(std::vector<T*> v)
{
for (vector<T*>::iterator i = v.begin(); i != v.end(); i++)
{
delete *i;
}
}

using C++ vector as a buffer

I am using vector as a buffer to store some objects to be later appended to another vector.
std::vector<Link*> buffer_vector;
std::vector<Link*> main_vector;
main_vector.insert(main_vector.end(),buffer_vector.begin(),buffer_vector.end());
The process of filling the buffer and appending occurs repeatedly. Therefore I need to clear the buffer at every iteration. My concern is if I use .erase or .clear methods to clear the buffer, the objects in the main vector will be deleted. Is this assumption correct?
If yes, is there a workaround for that?
Thank you
vahid
Your concern is incorrect.
main_vector.insert(main_vector.end(),buffer_vector.begin(),buffer_vector.end()); copies the elements from buffer_vector into main_vector.
These copied elements are totally separate from the original elements, and so they are not affected by modifications to the elements that they were copied from.
If you mean clearing the buffer_vector will clear the contents in main_vector, no they are separate copies.
Insertion into a vector is done by copy. In other words, you're pushing a copy of the element into the new vector. A different copy will be deleted when the buffer vector is cleared.
(This assumes that you're pushing the same type into both vectors, and not something like a pointer to an element to a vector in the first vector into the second vector.)
You are copying the elements into the vector, and they will stay there until you delete them.
In the code you show, you are storing pointers into objects. The pointers will be copied, and nothing will happen to the allocated memory when the original vector is cleared or the elements erased.
Actually nothing will happen to the pointed memory when the second vector goes out of scope!! Your program leaks memory, you need to manually manage the pointers or else choose an appropriate smart pointer that you can use inside a vector (consider std::unique_ptr or std::shared_ptr from C++11 or the equivalents from boost or TR1).
You may be confused about how a vector manages its objects. When you push_back into a vector, you make a copy. When you push_back a pointer, it copies the pointer. When you call clear() or erase() it deletes the pointer but not the object the pointer points to.
I am assuming you new'ed all your Link objects and added them to the vector container. You will need to delete them yourself, the container will not destroy the pointed to object for you (even if the container itself is destroyed when it goes out of scope). If you clear() both your containers before calling delete on all your Link objects, you will cause a memory leak (assuming you are not keeping pointers to Link objects somewhere else).

Removing something from a STL container without deconstructing it

Ok, I'm using C++ STL containers (currently vector<customType*>).
Now I need to remove elements from the container,
but using erase deconstructs the object, which is bad, since I'm taking it off one, and putting it onto a variable doing some processing then onto another one.
At the moment my code is quite nasty, and I'm just putting NULL in its place after I've read it, into my variable, and then putting a if (Q[ii]NULL) continue.
But this is not so great.
If you have a container of pointers (which it sounds like you do since you are assigning NULL to "erased" elements), then erasing an element from the container does not delete the pointed-to object. You are responsible for doing that yourself.
If you have a container of objects (well, non-pointer objects), then you need to copy the element out of the container before you erase it.
You can't really remove the element from the vector without destroying it. If your vector stores pointers, you can remove the pointer to the element, which won't actually destroy the element itself.
STL container operations have copy semantics. So any time you add or remove elements, the constructor or destructor will get called accordingly (assuming a non-primitive type). And if the vector gets resized in the process, all objects will be copy-constructed and the originals destructed. There's no way to avoid all this copying.
The only way to avoid the overhead is to have a vector of (smart) pointers instead of objects.