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.
Related
I'd like to have something equivalent to a C++ std::vector, where the underlying objects are immutable. So I can push_back() items to add them to the vector, etc. An actual std::vector maintains an array underneath that's bigger than the size of the vector, filled with default constructed objects, and when you push_back(), it does an assignment to an element in the array. My immutable objects don't have a default constructor, and assignment is a mutating operation, so that's out too.
I can do a vector<boost::optional<T>>, but that's a messy interface because I only want to put validly constructed objects into the vector, and only get those out of the vector.
I thought boost had something like this, but I couldn't find it. Does something like this exist?
Your conception of how the vector works is incorrect.
The vector uses the allocator to allocate raw memory. That raw memory does not contain default constructed objects--it's just raw memory.
When you do a push_back (for example) it then uses a placement new to construct an object into the raw memory. Likewise, when you erase an object, it will end up directly invoking its destructor to turn the object back into raw memory.
With a current (C++11 or later) implementation of std::vector, your object doesn't need to support default construction or assignment. Supporting move construction and move assignment should be sufficient. To put them to use, you'd want to use emplace_back instead of push_back though.
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>>.
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).
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.
What I am trying to do is essentially create two vectors of objects, where some objects are entered into both lists and some are just entered into one. The first problem I found was that when I used push_back() to add an object into both lists the object was copied so that when I changed it from one list the object did not change in the other. To get around this I tried to create a list of pointers to objects as one of the lists. However when I accessed the pointer later on the data seemed to be corrupted, the data member values were all wrong. Here are some snippets of my code:
Definition of vectors:
vector<AbsorbMesh> meshList;
vector<AbsorbMesh*> absorbList;
...
Adding an object to both:
AbsorbMesh nurbsMesh = nurbs.CreateMesh(uStride, vStride);
// Add to the absorption list
absorbList.push_back(&nurbsMesh);
// Store the mesh in the scene list
meshList.push_back(nurbsMesh);
Accessing the object:
if (absorbList.size() > 0)
{
float receivedPower = absorbList[0]->receivedPower;
}
What am I doing wrong?
There's some details missing, but at a guess.
nurbsMesh goes out of scope between the push_back and the absorbList[0]->receivedPower.
So now your vector of pointers contains a pointer to an object that doesn't exist anymore.
Try adding a copy constructor to your AbsorbMesh class and adding to your vector like this.
absorbList.push_back(new AbsorbMesh(nurbsMesh));
meshList.push_back(nurbsMesh);
don't forget to delete the objects in absorbList, like this
for(vector<AbsorbMesh*>::iterator it = absorbList.begin(); it != absorbList.end(); it++) {
delete it;
}
Or store a shared pointer in your vector instead of a bare pointer. Boost has a good shared pointer implementation if you're interested. See the docs here
If you want to have updates to items in one vector modify objects in the other vector, then you'll need to store pointers in both vectors.
Using your original requirements (updating an item in one vector affects items in the other vector, here's how I'd do it with a boost shared pointer. (WARNING, untested code)
vector<boost::shared_ptr<AbsorbMesh> > meshList;
vector<boost::shared_ptr<AbsorbMesh> > absorbList;
boost::shared_ptr<AbsorbMesh> nurb = new AbsorbMesh(nurbs.CreateMesh(uStride, vStride));
meshList.push_back(nurb);
absorbList.push_back(nurb);
...
...
if (absorbList.size() > 0)
{
float receivedPower = absorbList[0].get()->receivedPower;
}
You are storing the address of an object allocated on stack. The nurbsMesh object gets destroyed as soon as your method which does push_back() ends. If you try to access this pointer afterwards the object is already destroyed and contains garbage. What you need is to retain the object which remains even after the function goes out of scope. To do this allocate the memory for the object from heap using new. But for every new you should have a corresponding delete. But in your case you'll have problems deleting it as you are pushing the same pointer into two vectors. To solve this, you would require some type reference counting mechanism.
Object is deleted when you try get pointer from vector.
Try do
vector.push_back(new Object);
Once you have fixed the problem that others mentioned (storing a pointer to an object that's on the stack), you're going to run into another issue. A vector may reallocate, which results in its contents moving to another location.
The safe way to do this, then, is to store pointers in both vectors. Then, of course, you need to ensure that they get deleted... but that's C++ for you.
absorbList.push_back(&nurbsMesh); is wrong
absorbList save pointer to local object. When nurbMesh is destroyed you can not write
absorbList[0]->
However when I accessed the pointer later on the data seemed to be corrupted
When you put something on a vector, the vector might move it from one physical location to another (especially e.g. when the vector is resized), which invalidates any pointer to that object.
To fix that, you'll need to store a pointer (possibly a 'smart pointer') in both vectors (instead of having one vector contain the object by value).
If you're going to do this, it might be a good idea to disable the object's copy constructor and assignment operator (by declaring them as private, and not defining them) to ensure that after an object is created it cannot be moved.
There are several things wrong with your example
AbsorbMesh nurbsMesh = nurbs.CreateMesh(uStride, vStride);
This object is allocated on the stack. It is a purely local object. This object is going to be destroyed when you reach the end of the current block surrounded by {}.
absorbList.push_back(&nurbsMesh);
Now you get the pointer to the object that most likely will be destroyed.
meshList.push_back(nurbsMesh)
And this copies an entirely new object on the vector.
It is equally wrong to push the object on the vector first and then push a pointer to the object on the vector using absorbList.push_back( &meshList.back() ) because vector::push_back will reallocate the whole vector, invalidating all pointers.
You might be able to create all AbsorbMesh objects first, push them onto a vector, and then get the pointers to these objects in the vector. As long as you don't touch the vector, you'll be fine.
Alternatively, create objects on the heap using new AbsorbMesh() but make sure to call delete on each pointer thus created. Otherwise you have a memory leak.
Third solution, avoid the trouble and use smart pointers that take care of object destruction for you.
First, as everybody else points out, you can't allocate objects on the stack (i.e., other than by new or something similar), and have them around after leaving the scope.
Second, having objects in an STL container and maintaining pointers to them is tricky, since containers can move things around. It's usually a bad idea.
Third, auto_ptr<> simply doesn't work in STL containers, since auto_ptrs can't be copied.
Pointers to independently allocated objects work, but deleting them at the right time is tricky.
What will probably work best is shared_ptr<>. Make each vector a vector<shared_ptr<AbsorbMesh> >, allocate through new, and at a slight cost in performance you avoid a whole lot of hassle.