Why write a Virtual Destructor but not a Virtual Constructor?
Updated: May 26, 2021
"Of course base class destructors should be virtual if you're going to delete polymorphically (i.e., delete via a pointer to base)!" - Herb Sutter
In this blog, I will discuss a very basic concept related to destructor. It is a basic concept but important. In any Early or Mid-level C++ interview, you can expect a question from this.
So the structure of the blog is as below
You can create any object in many ways. You can create a static object, non-static object, dynamically allocate memory for an object.
When you create an object in any way, there is a function get called Constructor. This constructor takes care of memory allocation for the object and initialization of member data.
A constructor performs its work in this order (Ref):
It calls base class and member constructors in the order of declaration.
If the class is derived from virtual base classes, it initializes the object's virtual base pointers.
If the class has or inherits virtual functions, it initializes the object's virtual function pointers. Virtual function pointers point to the class's virtual function table to enable the correct binding of virtual function calls to code.
It executes any code in its function body.
When your created object goes out of scope or dies for any reason, It calls a special function called Destructor. This function takes care of all kinds of cleanup. Your object may be holding any recourse or has any dynamic memory allocated. This is the last chance or place to free that memory or resource.
If for any reason your object failed to do that, there is a high risk of memory leak or resources will not be available for use further.
When an object goes out of scope or deleted, the sequence of events in its complete destruction is as follows(Ref):
The class's destructor is called, and the body of the destructor function is executed.
Destructors for nonstatic member objects are called in the reverse order in which they appear in the class declaration. The optional member initialization list used in the construction of these members does not affect the order of construction or destruction.
Destructors for non-virtual base classes are called in the reverse order of declaration.
Destructors for virtual base classes are called in the reverse order of declaration.
Before jumping to a virtual destructor, let's understand the issue we have without a virtual destructor.
Consider the below code:
Here we have a base class and a derived class that is inheriting base as public. In main we have a base pointer that is pointing to a derived class object. This is Upcasting.
Upcasting can cause object slicing when a derived class object is passed by value as a base class object
base::constructor derived::constructor base::destructor
If you run this program and observe, you will see only the destructor of the base class get called. What about derived class destructor?
It did not get called.
What is the problem here?
To understand the actual problem, consider the below code.
base::constructor derived::constructor Add: 015A5B10 value: 10 base::destructor ptr is pointing to 015A5B10 and value is 10
If you have noticed, the derived class has a pointer member variable that gets memory allocated dynamically in the derived class. We are deleting this dynamic memory in the derived class destructor.
In main(), to explain the point, there is an 'int *ptr' which is pointing to the memory held by derived class member variable 'x'.
When deleting b, the destructor of derived is not get called and the memory pointed by x is not freed. That's why still ptr is pointing to the same memory and contains the value.
This condition leads to memory leak(Undefined behavior) because 'delete x' will never execute.
So, what is the solution?
Now when you know the issue, it's the right time to talk about virtual destructor. Consider the below code.
base::constructor derived::constructor Add: 00DC9080 value: 10 derived::destructor base::destructor ptr is pointing to 00DC9080 and value is -572662307
(If you are running this program on some online compiler, you may get 0 instead of garbage value for 'ptr'.)
So what happened behind the scene?
When the program executed 'delete b', it calls the destructor of the base class because 'b' is a base class pointer. It checks if the base class destructor is virtual. Now it will give a call to the derived class destructor. Derived class destructor is deleting memory associated with 'x'.
So by making the base class destructor virtual this program is resolving the memory leak issue in the previous program.
Sounds good and clear. But
Why there is no virtual constructor?
In C++, there is no concept of the virtual constructor.
But there should be some reason for not having a concept like this in such an old and strong language.
When you write 'virtual' before any function declaration, it allows you to call the function only knowing the interface, not the type of object.
To create an object, you need to know the exact type of the object.
Constructor is the function that takes care of object creation. Before the construction of an object, there is no vtable present. The compiler adds some extra code to the class when you make any function virtual. It creates a vptr and points it to Vtable. Once this is done, then only you can use any virtual function. There is no vptr that exists before the constructor call, so it is impossible to resolve the virtual constructor.
This is the reason virtual constructor is not possible in C++.
You should always make your destructor virtual if you’re dealing with inheritance and going to delete objects polymorphically.
Consider you wrote a base class without making destructor virtual and there are many derived classes inheriting this base class. Now after few months, some new developers came in and added a dynamic memory in one of the derived classes and he is deleting it in the respective destructor. He may not be aware of the base class.
If you are using a base class pointer to handle all your derived objects. Your program will be full of memory leaks. Now think about the situation if you have many derived classes like this. It will be a horrible nightmare.
You may make destructors of all classes virtual, to give flexibility of using any class as a base class.
This has some overhead because adding a virtual function in a class will create a VTABLE for the class and add a Vptr to each object. And the resolution may be an extra overhead. It may be better than a memory leak. So chose wisely.
If you want to use a class as a base class, you should make the destructor virtual.
If the class is intended not to be a base class, make this class final. This will prevent other classes to use this class as a base.
You can't have a virtual constructor in any class.
If you want to achieve virtual construction, you can refer to "Factory design pattern".