Reputation: 142
I've got a std::list
of Entity
objects (on-screen objects). In one class, EntityContainer
, I have a list of pointers to different entities. When a EntityContainer
is destructed, I want all of the entities in that list to be destructed as well. How can I do this, while also avoiding the iterator errors that result in deleting members of the list?
EntityContainer::~EntityContainer()
{
// Destroy children
int numChildren = children.size();
for (int i = 0; i < numChildren; i++)
{
Entity*& child = children.back();
delete child;
}
}
The above causes a null pointer access violation in std::list::clear(), which is called during the destruction of EntityContainer
, as it is a member variable of that object. I would belive this is because I have deleted the objects in its list, so of course it will try to access them when deleting them. However, my problem is that if I simply leave it, and allow the list of clear()
without explicitly deleting the objects within it, their destructors are never called. I can only assume this is because the list only destroys the pointers in the list, not the objects that the pointers are pointing to. That's mostly as assumption though - I may be wrong. What would you do?
Upvotes: 3
Views: 16792
Reputation: 31
Your loop is giving an error mostly because of this
int numChildren = children.size();
for (int i = 0; i < numChildren; i++)
{
Entity*& child = children.back(); // this always returns the last element on the last, it does not remove it. You are trying to delete an already deleted pointer
delete child;
}
like it has been suggested above, try something like
for(auto itr = children.begin(); itr != children.end(); ++itr)
{
Entity* child = *itr;
delete child;
}
Upvotes: 1
Reputation: 8171
Your loop isn't calling delete on every pointer in the list. It's repeatedly calling delete only on the last pointer. That's the cause of your access violation.
delete
won't remove anything from a list
because it doesn't know anything about it.
You need to do something like
for(auto itr = children.begin(); itr != children.end(); ++itr)
{
Entity* child = *itr;
delete child;
}
Or whatever your preferred loop syntax would be.
Alternatively, you could make it a list
of std::unique_ptr<Entity>
so that the deallocation is managed automatically when the list is cleared or entries are erased.
Upvotes: 3
Reputation: 109119
Assuming children
is defined as
std::list<Entity *> children;
you can delete
the elements using:
for(auto&& child : children) {
delete child;
}
children.clear(); // not really needed since this code is in the destructor
There is no issue here with invalidating any iterators because you're not actually removing any elements from the list
, only destroying the objects the list elements point to. The list
will still contain the same number of elements after the for
statement completes, only they'll be pointing at invalid memory at that point.
But really, don't use a container of raw pointers. Define children
as
std::list<std::unique_ptr<Entity>> children;
And then you can get rid of the destructor definition.
Upvotes: 14
Reputation: 179392
std::list<Entity *>
will not attempt to access any of the pointed-to objects during destruction.
Your iterated deletion code appears wrong. What you should do is just loop over the list and delete everything:
for(Entity *ptr : children) {
delete ptr;
}
and then leave the list to clean up (deallocating the internal list of pointers).
Upvotes: 3