When to Use Virtual Destructors In C++?

When to Use Virtual Destructors In C++?

Answer: In C++, using virtual destructors is essential when polymorphism, inheritance, dynamic memory allocation, and smart pointers are involved.

The virtual destructor in C++ is a base class destructor that is defined using the keyword virtual. It ensures that, while deleting a derived class object using a pointer of base class type, both base class and derived class destructors are called in the proper order. 

This will help avoid memory leaks and provide for proper cleanup, especially when using the concepts of polymorphism, inheritance, dynamic memory allocation, and smart pointers. In this article, we will discuss more on virtual destructors and their usage.

Table of Contents:

How Does Virtual Destructor Work in C++?

In C++, it is important to know how destructors work in inheritance so that resources can be handled correctly.

  • When you delete an object created in a derived class through a base class pointer, the suitable destructor for both base and derived classes is called. 
  • For a class with a virtual destructor, the application will first call the derived class destructor, and finally, the destructor for the base class will be called. The destructor function shares the same name as the class, preceded by a tilde (~). 

Use Cases of Virtual Destructors In C++

There are four major use cases for virtual destructors in C++. Here are some use cases:

1. When You Use Polymorphism and Inheritance Together 

When polymorphism and inheritance are used together, the base class destructor should be declared as virtual in C++. If you want to delete an object of a derived class that is accessible through a base class point, then ensure that the base class destructor is declared as virtual. If not declared virtual, deletion of an object using a base class pointer will call the base class destructor, but not the derived class constructor. This eventually results in the leakage of memory or improper cleanup of resources in the derived class.

Example: 

Cpp

Output: 

Explanation: In this example, deleting a derived class object through a base class pointer, the virtual destructor calls both the derived and base destructors.

2. Handling Object Deletion With Base Class Pointers

If you use a base class pointer to delete an object created from a derived class, make sure the base class destructor is virtual. If the destructor is not virtual, deleting the object through the base class pointer will simply call the base class’s destructor, not the derived class’s destructor.

This may lead to either memory leaks or improper clean-up resources (that is possible by the derived class)

Example:

Cpp

Output: 

Explanation: In this case, when there is a virtual destructor in the base class, it guarantees that if you’re deleting an object of the derived class using the pointer of the base class, the destructors of the base and derived class will be executed in the appropriate order.

  • A derived class destructor is called first.
  • The base class destructor is called the second. 

3. Handling Dynamic Memory Allocation in Derived Class 

If a class is allocating some resources (using new or using malloc), its destructor will need to be virtual. If the destructor is not virtual and the delete is through the base class pointer, only the base class destructor will be called and not the derived class destructor, resulting in the situation of a memory leak.

Example:

Cpp

Output: 

Explanation: This code demonstrates the correct use of destructors in C++ for memory management. When deleting a Derived object through a Base pointer, both the Derived and Base destructors are called, freeing dynamically allocated memory.

4. Managing Memory With Smart Pointers 

In using smart pointers such as std::unique_ptr and std::shared_ptr,  the base class destructor must be virtual in polymorphic cases to ensure proper cleanup. If the base class destructor isn’t virtual, when using smart pointers to deallocate memory, only the base destructor runs. This can cause the memory allocated by the derived class to not be properly released.

Example: 

Cpp

Output: 

Explanation: The ‘std::unique_ptr<Base>’ takes care of the derived object in the provided code. A virtual destructor in the ‘Base’ class ensures that both the  ‘Derived’ and ‘Base’ destructors are called correctly when the pointer goes out of scope. This property frees the memory from the leaks.

Advantages of Virtual Destructors in C++

  • Proper Resource Cleanup: The Virtual destructors in C++ generally ensure both derived and base class destructors should be called when you want to delete an object through a base class pointer that simply prevents resource leak problems.
  • Prevents Memory Leaks: It is very important for polymorphism, as without a virtual destructor deleting a derived class object through a base class pointer may cause memory leaks.
  • Ensures Correct Destructor Order: It also ensures that the derived destructor runs first, followed by the base destructor to avoid any incomplete cleanup and undefined behavior.

Performance Considerations when using Virtual Destructors

Virtual destructors are important for correctly cleaning up objects in polymorphic scenarios, but they can slightly impact performance due to the virtual function mechanism.

  • Vtable Lookup Overhead: Virtual destructors generally search in vtable for the correct destructors to call at runtime. This simply introduces the slower functions over the non-virtual functions, majorly on for loops.
  • Impact on Deletion of Objects in Deep Inheritance Hierarchies: Deletion by base class pointers guarantees all destructors along the hierarchy are called correctly. This ensures proper cleanup of all the objects with inheritance hierarchy.
  • Negligible Impact for Most Applications: The impact of virtual destructors is generally small. For the majority of real-world applications, the benefits from clean-ups and the management of resources far outweigh the small performance cost.

Common Challenges Occurred by Virtual Destructors in C++

1. Memory Leaks Due To Missing Virtual Destructor

A base destructor will need to be virtual when polymorphism is being used. Otherwise, when you delete the derived class instance through the base class pointer, only the base destructor gets called, causing garbage collection and partial cleanups.

2. Unnecessary Overhead Without Polymorphism

If a class doesn’t make use of polymorphism, the addition of the virtual destructor adds vtable overhead, making the object larger and slowing destructor calls unnecessarily.

3. Incorrect Destructor Call Order 

A virtual destructor ensures the derived destructor is called before the base destructor, preventing leaks. If not supplied, the base destructor may run before the derived destructor, causing partial cleanups and potential undefined behavior.

Conclusion 

Implementing a destructor is important for the proper cleanup in a polymorphic scenario. When working with base-class pointers to delete derived-class objects,  a virtual destructor ensures that the destructors of both the derived and base classes are called in the correct order. This saves from memory leak issues that arise due to dynamic memory or resources in the derived class. Virtual destructors are also especially important in the context of smart pointers since they enable their use for efficient memory management. 

FAQs

1. What is a virtual destructor?

The virtual destructor C++ is a base class destructor that is defined using the keyword virtual. It ensures that, while deleting a derived class object using a pointer of base class type, both base class and derived class destructors are called in the proper order.

2. Can I have a virtual destructor in an abstract class?

Yes, you can have a virtual destructor inside an abstract class. It makes sure that the destructors of all derived classes are called when an object is deleted through a base class pointer.

3. Is it necessary to make all destructors virtual

No, only the destructors in base classes that will be used polymorphically need to be virtual.

4. What’s the difference between a virtual and non-virtual destructor?

A non-virtual destructor leads to calls for the destructor for the base class only in case an object is deleted through a base class pointer, ignoring the call to derived class destructors. The virtual destructor guarantees the calling of the right constructor, whether from the base or derived class, and thus it is helpful against memory/resource problems.

5. Can a virtual destructor be called explicitly?

Well, you can explicitly call your virtual destructor like any other function. However, it is normally used in context to delete objects polymorphically.

About the Author

Technical Research Analyst - Full Stack Development

Kislay is a Technical Research Analyst and Full Stack Developer with expertise in crafting Mobile applications from inception to deployment. Proficient in Android development, IOS development, HTML, CSS, JavaScript, React, Angular, MySQL, and MongoDB, he’s committed to enhancing user experiences through intuitive websites and advanced mobile applications.

Full Stack Developer Course Banner