In this article, we are going to deep dive into the virtual destructors in C++. In the end, you will find yourself getting a vast knowledge about what they are. If you have previous knowledge of the 'new' keyword then this will help you to go to the most depth of logic behind them.
What is Virtual Destructor in C++?
As we know, a destructor is a special member function that is called when the object is destroyed automatically. Its purpose is to free the resources that the object has acquired to make them available for other parts of the program.
A virtual destructor in C++ is a destructor that has the 'virtual' keyword in its declaration. Yes, we can have a virtual destructor in C++ just like any other virtual function. These virtual functions can be overridden by a function with the same name.
Didn't understand? It's totally fine.
Let's go step by step! Look at the C++ code below:
#include<iostream> using namespace std; class A { int a;
int f1(){...} }; class B : public A { int b;
int f1(){...}
}
int fun();
int fun(){
A *p = new B;
p->f1(); //CORRECT
p->f2(); //ERROR
}
int main(){
fun();
}
In the above code, parent class A is inherited publicly by child class B. In the main function, we call the fun function in which we are initializing the pointer of base class A type to the object of the child class B's object. Due to early binding, since p is of type class A, it can access only the members of class A only. Therefore p->f2() gives us the error itself.
Consider the below code to understand the undefined behavior we are talking about:
// CPP program without virtual destructor // causing undefined behavior #include<iostream> using namespace std; class base { public: base() { cout << "Constructing base\n"; } ~base() { cout<< "Destructing base\n"; } }; class derived: public base { public: derived() { cout << "Constructing derived\n"; } ~derived() { cout << "Destructing derived\n"; } }; int main() { derived *d = new derived(); base *b = d; delete b; getchar(); return 0; }
Output:
Constructing base Constructing derived Destructing base
As discussed above, first a pointer is created named 'd' of child class type which has the address of the object of child class object, and then the pointer of base class type is also pointing to the same object of child class type.
Delete b results in calling the destructor of the base class only because of the early binding, when code compiles, it sees the pointer of type parent class so the pointer b has access only to the base class members not to the child class members. This causes memory leakage problems in C++. To avoid this problem we use virtual destructors.
Making base class destructor virtual guarantees that the object of the derived class is destructed properly, i.e., both base class and derived class destructors are called. For example:
// A program with virtual destructor #include<iostream> using namespace std; class base { public: base() { cout << "Constructing base\n"; } virtual ~base() { cout << "Destructing base\n"; } }; class derived : public base { public: derived() { cout << "Constructing derived\n"; } virtual ~derived() { cout << "Destructing derived\n"; } }; int main() { derived *d = new derived(); base *b = d; delete b; getchar(); return 0; }
Output:
Constructing base Constructing derived Destructing derived Destructing base
Why do we need Virtual Destructor in C++?
Virtual destructor in C++ works in a way that ensures the correct order of invoking destructors when the object of the derived class goes out of scope or is deleted. If the order of invoking is incorrect, it will lead to a problem known as a memory leak.
When we use a virtual destructor inside the base class, it will call the destructor of the child class, which will ensure that the child class's object should be deleted so there might be no memory leakage. Although the output of the following program may be different on different compilers.
Please Note that the derived class's destructor is invoked indirectly using the vptr (Virtual Pointer Table).
As a guideline, any time you have a virtual function in a class, you should immediately add a virtual destructor (even if it does nothing). This way, you ensure against any surprises later.
Conclusion
So, in this article, we understood all about virtual destructors in C++ & how early binding and late binding occur, and how we can control them using the 'virtual' keyword. Congratulations on getting this far! Now give yourself a pat on the back. Good job!