In C++, a virtual destructor is used to free up the memory space allocated by the derived class object or instance while deleting instances of the derived class using a base class pointer object. A base or parent class destructor uses the virtual keyword that ensures both the base class and the derived class destructor that will be called at run time, but it calls the derived class first and followed by the base class to release the space occupied by both destructors.
Syntax
It has the following syntax:
class Base {
public:
virtual ~Base(); // Virtual destructor
};
Simple Virtual Destructor Example
Let us take an example to demonstrate the virtual destructor in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class Base {
public: //Access Modifier
virtual ~Base() {
cout << "Base Destructor\n";
}
};
class Derived : public Base { //Derived Class
public:
~Derived() {
cout << "Derived Destructor\n";
}
};
int main() { //Main Function
Base* ptr = new Derived(); // Base pointer to Derived object
delete ptr; // Delete via base pointer
return 0;
}
Output:
Derived Destructor
Base Destructor
Explanation
In this example, the ptr is a base class pointer that is used to point to a Derived object. The base has a virtual destructor. When the delete ptr is executed in the main function, the Derived class's destructor is called first, followed by the Base class's destructor. It ensures proper cleanup of both derived and base parts of the object.
Why do we use virtual destructor in C++?
When an object in the class goes out of scope or the execution of the main function is about to end, a destructor is automatically called into the program to free up the space occupied by the class' destructor function. When a pointer object of the base class is deleted thacpp tutorials to the derived class, only the parent class destructor is called due to the early bind by the compiler. In this way, it skips calling the derived class' destructor, which leads to memory leak issues in the program.
If we use the virtual keyword preceded by the destructor tilde (~) sign inside the base class, it guarantees that first the derived class' destructor is called. After that, the base class' destructor is called to release the space occupied by both destructors in the inheritance class.
How Virtual Destructors Work Internally
In C++, compilers generate vtable entities during their creation of virtual function classes together with event destructors. All objects of the class maintain secret vtable pointers inside their structure.
C++ performs runtime type checks through vtable access to determine what destructors need execution when the destructor has virtual designation.
Usage of Virtual Destructor
There are several usage of virtual destructor in C++. Some of them are as follows:
1) Polymorphic Deletion via Base Pointer
A base class pointer attempts to delete a derived class object that will execute only its base class destructor if the virtual destructor is not available. The derived class's destructor fails to perform, which produces resource leaks.
C++ Polymorphic Deletion via Base Pointer
Let us take an example to illustrate polymorphic deletion via base pointer in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class Base {
public:
~Base() {
cout << "Base Destructor\n";
}
};
class Derived : public Base {
public:
~Derived() {
cout << "Derived Destructor\n";
}
};
int main() { //Main Function
Base* ptr = new Derived();
delete ptr; // Undefined behavior
return 0;
}
Output:
Base Destructor
Explanation
In this example, the base class fails to contain a virtual destructor declaration. The delete ptr statement triggers the Base destructor and skips any Derived destructor execution. After that, derived objects would suffer from resource leaks because these objects were unable to release dynamic memory or file handles.
2) Interface-like Base Class
In C++, virtual destruction declaration on the base class allows us to use the vtable to execute destructor sequences at runtime properly.
Example
Let us take an example to illustrate the interface-like base class in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class Base {
public: //Access Modifier
virtual ~Base() {
cout << "Base Destructor\n";
}
};
class Derived : public Base { //Derived Class
public:
~Derived() {
cout << "Derived Destructor\n";
}
};
int main() { //Main Function
Base* ptr = new Derived();
delete ptr; // Safe deletion
return 0;
}
Output:
Derived Destructor
Base Destructor
Explanation
In this example, both destructors are called in the correct order. After that, complete object destruction is guaranteed.
3) Managing Dynamic Resources in Derived Classes
In C++, derived classes allocate resources like memory or file handles. If the destructor isn't virtual in the base class, it will never be released if deleted polymorphically.
C++ Example to Manage Dynamic Resources in Derived Classes
Let us take an example to illustrate how to manage dynamic resources in derived classes in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class Base {
public:
virtual ~Base() {
cout << "Base cleaned up\n";
}
};
class Derived : public Base {
private:
int* data;
public:
Derived() {
data = new int[5];
cout << "Derived allocated memory\n";
}
~Derived() {
delete[] data;
cout << "Derived released memory\n";
}
};
int main() { //Main Function
Base* b = new Derived();
delete b; // Proper cleanup
return 0;
}
Output:
Derived allocated memory
Derived released memory
Base cleaned up
Explanation
In this example, we prevent memory leaks when resources are managed in derived classes.
C++ Example to display a Class Destructor without using a Virtual Destructor
Let us take a program to display a class destructor's undefined behaviour without using a virtual destructor in C ++.
Example
#include<iostream>
using namespace std; //using standard namespace
class Base
{
public: //Access Modifier
Base() // Constructor function.
{
cout<< "\n Constructor Base class";
}
~Base() // Destructor function
{
cout<< "\n Destructor Base class";
}
};
class Derived: public Base
{
public:
Derived() // Constructor function
{
cout << "\n Constructor Derived class" ;
}
~Derived() // Destructor function
{
cout << "\n Destructor Derived class" ; /* Destructor function is not called to release its space. */
}
};
int main() //Main Function
{
Base *bptr = new Derived; // Create a base class pointer object
delete bptr; //delete pointer
}
Output:
Constructor Base class
Constructor Derived class
Destructor Base class
Explanation
In this example, we have taken a Base* pointer to create a Derived object. We also took a base class. After that, it deletes the pointer object occupied by the base class' destructor and the derived class's destructor. The base class pointer only removes the base class's destructor without calling the derived class' destructor in the program. Hence, it leaks the memory in the program.
C++ Example to display a Class Destructor using a Virtual Destructor
Let us take a program to display a class destructor's behaviour using a virtual destructor in C ++.
Example
#include<iostream>
using namespace std; //using standard namespace
class Base
{
public:
Base() // Constructor member function.
{
cout << "\n Constructor Base class"; // It prints first.
}
virtual ~Base() // Define the virtual destructor function
{
cout << "\n Destructor Base class";
}
};
// Inheritance concept
class Derived: public Base
{
public:
Derived() // Constructor function.
{
cout << "\n Constructor Derived class";
}
~Derived() // Destructor function
{
cout << "\n Destructor Derived class";
}
};
int main() //Main Function
{
Base *bptr = new Derived; // A pointer object references the Base class.
delete bptr; // Delete the pointer object.
}
Output:
Constructor Base class
Constructor Derived class
Destructor Derived class
Destructor Base class
Explanation
In the above program, we have used a virtual destructor inside the base class that calls the derived class' destructor before calling the base class' destructor and releasing the spaces or resolving the memory leak issue in the program.
Conclusion
In conclusion, the implementation of virtual destructors in C++ allows proper cleanup of objects within polymorphic class inheritance designs. Through virtual destructors the system can execute base and derived class destructors when using base pointer deletion. Only the base destructor receives a call when virtual destructors are missing, which puts memory at risk.
C++ Virtual Destructor MCQs
1) What is the primary purpose of declaring a destructor as virtual in a base class?
- Function overloading of destructors becomes possible through this declaration.
- To support constructor inheritance
- Deriving classes need the destructor to execute correctly when a base class pointer deletes the object.
- To improve runtime performance
2) When should we declare base class destructors as virtual?
- The class requires a virtual destructor if it will be used through polymorphism
- The requirement for a virtual destructor exists only when the class contains static members.
- The creation of objects occurs on stack memory.
- The rule exists to mandate virtual destructors in base classes that do not have members.
3) Why does a pure virtual destructor require definition even when marked =0?
- The C++ language does not enable pure virtual functions.
- Because destructors are always called during object destruction
- Compilers do not recognize virtual keywords in their operation.
- Base class objects don't actually exist in reality
4) What does the virtual keyword in a destructor ensure?
- The destructor cannot be overridden
- Objects of derived classes are the only entities able to access the destructor.
- During deletion, the correct derived class destructor gets invoked
- The destructor executes faster at runtime
5) When is a destructor in a base class not required to be virtual?
- A derived class that does not contain a destructor will use the base class destructor.
- When the base class is never used polymorphically
- The class contains only public data members
- When the destructor is defaulted