Sign in to follow this  
Farraj

vector's erase(): I did not know

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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
}
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Promit:

targetEntity never goes out of scope (base on my desing) and it's added back to the entity list when it's time comes. It's gets deleted before the game exites.

jpetrie:

Sorry for not exaplaining more but all entities are in a CEntityManager which is a Singleton. The targeEntity (I've named it m_tempEntity) is also a member in entity manager. I was just attempting a small trick to make Asteroid a bit intersting :P I was shocked when I learned that erase() doesn't just remove the entity (I should have figured that out, it's 'erase' after all :P)

And just a FYI, this was the only problem in the code. The game works fine now and I'm blasting asteroids like crazy :P

Share this post


Link to post
Share on other sites
I would agree. Why don't you want to know what you're doing wrong? Is it because of your hubris:
Quote:

And just a FYI, this was the only problem in the code.

I mean, certainly you couldn't be wrong twice. That would be impossible.

Share this post


Link to post
Share on other sites
Quote:
Original post by Farraj
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.


If you are able to do this, it means your vector stores pointers, which means your destructors were not being called in the first place.

Share this post


Link to post
Share on other sites
Just some constructive criticism from me [smile]

If you can't figure out the root cause of this problem, they I suggest that you should definitely do more reading on how C++ handles objects, and how STL containers such as std::vector do so as well.

The root cause here has nothing to do with your code, it has everything to do with your current level of understanding, I know some of the other members have ripped into your code, but their intent is to help you learn, so dont take it the wrong way.

So let me try explain it for you:
The life cycle of an object stored in a vector is directly controlled by the vector. When storing a object in an STL container, if more than a distinct data object (e.g. a struct with only primitive data members) then a copy constructor and a assignment operator are needed.

If the vector is being used as a check list or queue, e.g. simple a place to make a list of objects which need to be actioned upon (such as rendered) then pointers to existing objects should suffice, reduce data copying (copy a word size pointer rather than an entirely new copy of the object) however there are many caveats which go with this method, to do with memory management and stale pointers (pointers pointing to free'ed memory), and ofcourse preventing memory leaks.

I would suggest reading Thinking in C++ 2nd Edition, its a free PDF download, well written and has chapters 4 and 5 which cover STL to a good depth

Share this post


Link to post
Share on other sites
In my humble IMHO, what you should do is maintain 2 lists of entities:
- A list of pointers to entities (std::vector<Entity*>) that holds all your entities in the game. When an entity is removed from this list, it's forever gone. Depending on your game, you could use it for collision detection.
- Another list of pointers to entities that holds all the renderable entities. This list is a subset of the previous list. The elements are displayed to the screen, so you can easily add/remove entities when their coordinates are/aren't in the screen.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this