One the class X
destructor is called, the object it was called on ceases to be an object of type X
.
Suppose you have a class Y
which inherits from X
, and a class Z
that inherits from Y
. By the principles of object-oriented inheritance, every Y
is an X
. And every Z
is a Y
and an X
.
Look at what happens when deleting an object of type Z
if the destructors are called from most-derived to base:
First the Z
destructor executes.
The object stops being a Z
and
reverts to being only a Y
.
Then the Y
destructor executes. The
object stops being a Y
and reverts
to being only an X
.
Then the X
destructor executes. The
object stops being an X
, and is now
nothing at all (completely
destroyed).
Now consider what would happen if instead the base destructor was called first:
- First the
X
destructor executes. The object stops being an X
.
Now what is the object? It's not an X
, but it can't be a Y
either since a Y
is an X
. It can't be a Z
since a Z
is an X
too! The object has no clearly-defined type at this point, and so invoking another method on it, even another destructor, would lead to impossible-to-define behavior.
In more concrete terms: It's entirely possible that the Z
destructor or the Y
destructor needs to access something that is defined in the X
class. So the X
destructor must run last. Note that there's no problem in the other direction since the base class X
can't access anything in its derived classes.