Hi all:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
class Base
{
public:
Base(){ cout<<"Constructing Base";}
// this is a destructor:
~Base(){ cout<<"Destroying Base";}
};
class Derive: public Base
{
public:
Derive(){ cout<<"Constructing Derive";}
~Derive(){ cout<<"Destroying Derive";}
};
void main()
{
Base *basePtr = new Derive();
delete basePtr;
}
|
The code above produces the output:
1 2 3
|
Constructing Base
Constructing Derive
Destroying Base
|
After modifying the code, specifically, ONLY making the destructor virtual:
1 2 3 4 5 6 7 8
|
class Base
{
public:
Base(){ cout<<"Constructing Base";}
// this is a destructor:
virtual ~Base(){ cout<<"Destroying Base";}
};
|
The output becomes:
1 2 3 4
|
Constructing Base
Constructing Derive
Destroying Derive
Destroying Base
|
I don't understand why this is the way it is.
In my opinion, the output that makes sense should be:
1 2 3
|
Constructing Base
Constructing Derive
Destroying Derive
|
Why? (I won't explain the constructor, as everyone knows why, I guess)
In main:
1 2
|
Base *basePtr = new Derive();
delete basePtr;
|
According to late binding, at run time, the destructor of the object (i.e. object of type Derive) is called. And in the destructor, I didn't call the superclass' destructor. Then the superclass' destructor should NOT be called.
Okay, someone might argue, destructor is not an ordinary member function. Just like constructor, when you create an object of the Derive, firstly, the constructor of Base is called. How can you have a son without a father? And the same principle applies to destructor. A son object consists of one part from his father, and the rest is his unique part. How can you destroy only his unique part and leave the other one, his inherited part from his father, unchanged? It makes no sense because the idea is like burning a part of a dead body and leaving the other part in the world. (The undead?)
In fact, I think this is the only reasonable explanation in terms of design principle behind the phenomenon we observe in the sample code.
However, this explanation is self-contradictory:
If a destructor is having a similar status as the constructor, then it should be given the similar previlige. The constructor is called from outside to inside, layer by layer, and this rule is enforced by compilers, then the destructor should have the same previlige: destructor is called from inside to outside, layer by layer, enforced by compilers. Given that, in the first place, we shouldn't have observed
1 2 3
|
Constructing Base
Constructing Derive
Destroying Base
|
when the destructor of the Base class was NOT virtual.
I'm confused. I don't know which of the two perspectives I should take.
I hope someone could explain to me, and convince me from a design perspective. I don't really want to hear "It's a feature, so just live with it."
If you have a third perspective and it can answer the conflict I'm having, please do share it!
Thanks in advance! :D