Is it acceptable to call a destructor?

Started by
39 comments, last by yckx 11 years, 5 months ago
I read into it a bit, and the little information I got was from this source:

You can use a destructor explicitly to destroy objects, although this practice is not recommended.[/quote]

I have an enemy object that is destroyed upon being intersected with a projectile object. Instead of making a bunch of if statements in a for loop outside the object, wouldn't it be easier and cleaner to make the enemy object just destroy itself if it detects colliding with a projectile? Would it be okay, according to conventions, to call ~enemy INSIDE the enemy class, rather than having to code outside the enemy class?
Advertisement
While you can certainly do it, don't. Calling a class destructor inside the object itself basically always leads to undefined behavior. No other part of your code is aware that you've just invalidated every reference to that object. Are you 100% sure none of those references will be used after destruction?

You might want to review your collision code in general. While it might be "easier" to explicitly invoke a destructor it's probably a sign of code smell and should be dealt with sooner rather than later. If you post the code, we might be able to offer some advice on how better to do your collision.
How do you ensure that the destructor isn't called again when the object is actually about to be destroyed (for example goes out of scope, you release the memory by calling delete, or removing the object from an std::vector)?

What's wrong with having the outside logic taking care of that? If the enemy destroys itself, then that kind of implies that the enemy itself is responsible to handling the collision with projectiles. Isn't that a job for some outside component to handle collisions, like a physics component or something?
In general, it's poor form to directly call a destructor (save for the case where you're calling a parent class destructor from a child class desctructor).

Honestly I've never heard of anyone doing this and I can think of a lot of things that could go wrong. For instance, if after your ~Enemy() call, you attempt to manipulate the object (either outside the class or inside the class itself), you'll have an invalid pointer.

For instance:

[source lang="cpp"]...
// NOTE: Assumes you have a vector/array of enemies and a pointer to the projectile.

for( int i = 0; i < numEnemies; ++i )
{
if( checkCollision( enemies, pProjectile ) )
{
// Destructor will be called right here in onCollision().
enemies.onCollision(pProjectile);
}

// Now you have an invalid reference to your object and this call will produce only sadness (crash or undefined behavior).
enemies.someOtherFunction();
}

...[/source]
My code makes a picture of Sephiroth!
Don't call the destructor. It will be called automatically.

This is the case 99.9999999% of the time.


For the 0.00000001% of the time:

There is only one place you should ever call a destructor manually, and that is if you are using something called a "placement new".

Using a "placement new" is a fairly advanced topic that is almost never done in practice.

In general, it's poor form to directly call a destructor (save for the case where you're calling a parent class destructor from a child class desctructor).

No, don't even do it then. The language has guarantees that when a destructor is called, the base class destructors are automatically called as well. Do not call them manually during your destructor.
Also, keep in mind the difference between allocation/deallocation and construction/destruction. If the enemy holds projectile objects (e.g. an std::vector<projectile>) you don't need to do anything extra, all destructors are called automatically. But if it holds object *pointers* (e.g. std::vector<projectile*>) you need to decide on ownership. It's usually a good idea keep allocation and deallocation responsibility together. If it's the enemy object that allocates and stores projectiles, it should likely deallocate them too in its destructor.

[edit]

Ah, reading the OP I see the enemy doesn't hold projectiles. However, the principle that keeping allocation and deallocation together is still good advice here. And in any case, neither the birth and death of an object instance should be controlled by that instance itself. The former is just counter-intuitive and the latter too controversial.

openwar - the real-time tactical war-game platform

You also have to realize, when you figure out the enemy is dead, and needs to be removed, it will also have to be removed from the list of current enemies. So, whether this list is std::vector, std::list, linked list, or array, you're going to have to ensure that enemy isn't updated any more. Thus, you will HAVE to do something outside of the enemy object to handle it's removal. Like this:

Have your Enemy::Update() function return the status of the enemy (alive, dead, whatever), and remove it then:

// assuming EnemyList is a std container
for (int i = 0; i < EnemyList.size(); ) {
if (EnemyList.Update() == ENEMY_DEAD) {
// If The list stores pointers, you'll have to call delete here
EnemyList.erase(EnemyList.begin() + i);
}
else {
++i;
}
}

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

[Waits for someone to post some std::remove_if invocation in response to BeerNutts's code...]

[Waits for someone to post some std::remove_if invocation in response to BeerNutts's code...]

That was my first reaction as well:
[source]
EnemyList.erase(
std::remove_if(
std::begin(EnemyList),
std::end(EnemyList),
[](Enemy &e) {return e.Update() == ENEMY_DEAD;}),
std::end(EnemyList));
[/source]

This topic is closed to new replies.

Advertisement