C++ language feature: virtual destructor

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





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.

Correct.

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.

It's not that simple. It's always known at compile-time which constructors need to be called to construct a particular object, however it is generally not known at compile-time whether a Base pointer points to a Base object or a Derived object, so the compiler wouldn't know which destructor(s) to call. Hence why the class needs to be polymorphic in order to be able to call the right destructor. Making all classes polymorphic by default would be possible (Java does it), but it creates an overhead that is unnecessary and unwanted for a lot of classes (especially small ones).
C++ standard (12.4-6) wrote:
After executing the body of the destructor and destroying any automatic objects allocated within the body, a
destructor for class X calls the destructors for X’s direct members, the destructors for X’s direct base classes


So a destructor always calls it's parent's destructors. Compile a program, where you statically allocate an object of Derive type and notice that you get both destructors.
The reason why the first piece of code only gives Base destructor is that, on line 23, the program doesn't know what basePtr is pointing at (or at least isn't willing to use that knowledge).
When you declare a destructor or any method virtual, you tell the program to check what kind of object really called it.
This is why you may often hear never to derive from a class without a virtual destructor (even though such statement is slightly exaggerated).

too slow :-(
Last edited on
In my opinion, the output that makes sense should be:

Constructing Base
Constructing Derive
Destroying Derive


Even once you've sorted the virtual-ness of destructors, this output shouldn't be expected. Derive is derived from base, so when an instance of Derive is destroyed by Derive::~Derive(), those resources which it inherited from Base must also be destroyed by Base::~Base().
Thanks to all, for your time and efforts.
I was designing my classes in a wrong way, and that was the reason for me to question the way destructors across Base/Derived classes are called.

Now I see the rational behind the way it is, and indeed it was my problem in the design of classes.
Actually for this virtual destructor feature, I read somewhere an easy rule to know when you need it is look at your data members in your class. If you have pointers to some variables, chances are high you need to free them in your destructor. A simple rule but should work in general.
If a class is going to be inherited and used polymorphically, then you probably need make the destructor virtual. At some point, someone is going to inherit from it and write a nontrivial destructor and then if the base destructor isn't virtual you've got a problem.

The one circumstance in which you wouldn't have to do this is if you knew for certain that all derived classes would have trivial destructors (e.g. classes comprised only of stack variables/exception safe variables).
Last edited on
That is also why I think I read in Scott Meyers book the 3 rules ? If you have data members that are pointers to some variables, it will be wise you provide a proper copy constructor, assignment operator and a destructor.
If a class is going to be inherited and used polymorphically, then you probably need to make the destructor virtual.
fixed.
Last edited on
Thanks hamsterman. I shall update my post now.

Of course, seeing as C++ classes are supposed to be modular and reusable, it might be better to phrase it like this:

If a class is going to be inherited, then you probably need make the destructor virtual. At some point, someone is going to inherit from it and write a nontrivial destructor and then if the base destructor isn't virtual you've got a problem.

The circumstances in which you wouldn't have to do this are:
1) if you know for certain that all derived classes would have trivial destructors (e.g. classes comprised only of stack variables/exception safe variables).
2) if you know for certain that this class will never be used polymorphically

But then again, maybe it's not worth creating extra overhead for a contingency which may never actually arise...
Last edited on

But then again, maybe it's not worth creating extra overhead for a contingency which may never actually arise...


Java was created by someone who obviously know something about C++ and it's quirks and in the new language he chooses to do it away with.

Imagine you are writing business-centric applications and your brains will be busy thinking on how to implement business logic. Do you want to spend time on such technical stuff? The language should take care of them automatically for developers isn't it?

How many hours would have been spent in spotting such "bugs"? No wonder a business-centric application in C++ would take doubly or even triply long in total development time compared to a Java/Perl application.

I was wondering whether it was worth in the context of C++ ;)
I'm actually glad the the post triggered a heated discussion.

@Xander314:
Of course, seeing as C++ classes are supposed to be modular and reusable, it might be better to phrase it like this:


Ignore me if I'm wrong:
I detect your inclination to applying inheritance when you need to reuse code.

In fact, the biggest lesson I've learned from my design error that led me to question the way virtual destructors are called across Base/Derived classes:
Inheritance is NOT for code reuse even if sometimes we indeed have the benefit of writing less code from inheritance. Inheritance is for flexibility and interface enforcement.
Composite pattern is for code reuse.


My professor told me this, and I thought I knew this, but when it comes to my own design, I just somehow "forget" it.
Topic archived. No new replies allowed.