vector's erase(): I did not know

Started by
15 comments, last by Splo 15 years, 11 months ago
OK, I was making an Asteroid clone when the game crashed while it was checking for collisions between the entities. All the entities are stored in a vector and used when needed. The problem is that I always assumed that calling the erase() method only removes the entity from the vector. I just discovered that it also calls the entity's destructor (like using 'delete'), which pretty much breaks my design (just because I removed it doesn't mean I'm not using it still). My question is, how can I remove an entity from the vector without 'deleting' the entity?? Thanks.
Advertisement
Hello,

One thing you could do is to add an extra attribute to your class to indicate the particular object is removed and then check this in your render code. This way you don't have to call erase().

But I think that if your app crashes because a destructor is called, you probably have some issues with pointers referencing the same data, so I think you will have to check your copy-constructors (or use smart pointers)

kind regards
Uncle
Quote:Original post by Farraj
(just because I removed it doesn't mean I'm not using it still).



If your object’s destructor is called, it means the object is dead — you can’t use it and its storage will be deallocated briefly. (Unless you called the destructor explicitely yourself, which you did not.)

If you do vector< Entity > entities, then this vector will store copies of your entities — and it will copy them around as necessary for its internal stuff. So if you then do Entity e; entities.push_back( e ); you have created two entities — one explicitely, e, and the other one lives inside the vector. If you then .erase() the entity from the vector, you destroy this copy.

If you use vector< Entity* > entities, this vector will only store pointers to the entities — it will still make copies of the pointer as needed, but it will not copy or delete the Entity the pointer points to. However, this means you also have to keep track of your entities and allocate and deallocate them properly, so that you don’t store invalid pointers and you don’t leak memory.

With the help of Boost.SmartPtr, you can easily do vector< boost::shared_ptr< Entity > > entities; boost::shared_ptr< Entity > e( new Entity ); entities.push_back( e ); and proper deallocation will be taken care of automatically. Also, you could use boost::ptr_vector< Entity > instead to get the same behaviour.

If you already are storing pointers in your vector, then the destructor should not be called and there is something else going on — in which case it’d be helpful if you could provide some code that causes this behaviour.
If I'm not mistaken the vector owns the objects it contains. Therefore, if you say you have the same object outside the vector that you can use after it is removed from the vector, it must in reality be a copy? In that case, if the destruction of the original stops the copy from working, there must be a problem with how the copy was created. You should check your copy constructor and assignment operator (to ensure, for example, that they perform a deep copy if your objects contain dynamically allocated pointers).

If your vector holds pointers to objects, it does not delete what the pointer is pointing at. So any pointers outside the vector remain valid.

If you are storing references/iterators to objects in the container, these become invalid after the object is removed.
Quote:Original post by Farraj
I just discovered that it also calls the entity's destructor (like using 'delete'), which pretty much breaks my design (just because I removed it doesn't mean I'm not using it still).


Then your design is wrong. A vector stores copies of whatever is given to it, and holds itself free to (a) re-copy those things whenever it likes; (b) assume complete ownership of those copies; (c) destruct originals when it re-copies. If your object can't handle this, don't put it in a vector. But almost everything should be made to handle it.

If the vector represents all the entities in the "game space", then it makes perfect sense for them to cease to exist upon collision. If you're trying to store "prototypes" or something - or say you have some kind of "boss" object that "keeps coming back" - you probably want to re-create that object from scratch each time, instead of trying to reuse one.
Almost certainly the problem stems from not following the rule of three for the objects being stored in the vector.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
like UncleRemus suggested you need an Enable atribut on your entities, so you can disable the object without removing it from the vector. a simple bool will do, then on your render code:

std::vector <Entity*>::iterator i;for( i = _entities.begin(); i != _entities.end(); i++ ){  if( (*i)->isEnabled() )  {    //do the render    (*i)->draw();    //etc  }}
yet, another stupid signature..
Well, then I guess the answer is that there is no way to 'remove' a class instance without actully deleteing it.

I got a few good ideas from you guys, thanks.

Well, seeing that I only wanted to remove only 1 entity from the entity vector, I've taking this approch. I would replace the target entity with a 'dummy' entity then delete the dumy entity:

// the entity I want delete is in position i = 4

targetEntity = entityList.at(i);
entityList.at(i) = new CEntity("dummy");
entityList.erase(entityList.begin() + i);

I know it's not clean, but due to that I only need to remove one entity I guess this well do. No harm in hacking once and while :P

Thanks.
Uhh...so now targetEntity simply dies when it falls out of scope. How did that help anything?
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Quote:
targetEntity = entityList.at(i);
entityList.at(i) = new CEntity("dummy");

Warning flags. If this code compiles, then targetEntity is a pointer (because you're inserting a pointer). Which means that erase() isn't calling the CEntity destructor unless you've changed code since you first posted your problem. Which means the problem isn't what you think it is.

Post some code. You're doing something wrong and you don't seem to understand what.

This topic is closed to new replies.

Advertisement