To ensure safe and proper cleanup of derived class objects when deleting them through a base class pointer, use a virtual destructor in the base class under the following circumstances:
When to Use Virtual Destructors
- Polymorphic Base Classes
If your class is intended to be inherited from and you will delete objects of derived classes via a base class pointer, the base class must have a virtual destructor.
Example:
class Base {
public:
virtual ~Base() {} // Virtual destructor
};
class Derived : public Base {
public:
~Derived() { /* Cleanup resources */ }
};
Base* ptr = new Derived();
delete ptr; // Correctly calls Derived's destructor ✅
- Classes with Virtual Functions
If a class has any virtual functions, its destructor should also be virtual.
Rationale: Virtual functions imply the class is designed for polymorphism, so cleanup must work correctly. - Abstract Base Classes (ABCs)
Even if an abstract class has no other virtual functions, its destructor should be virtual:
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {} // Virtual destructor
};
When NOT to Use Virtual Destructors
- Non-Polymorphic Classes
If a class is not intended to be inherited from (e.g., utility classes), avoid the overhead of a virtual destructor. - Final Classes (C++11+)
If a class is markedfinal
, no virtual destructor is needed:
class NonInheritable final { /* ... */ };
Key Consequences of Missing Virtual Destructors
class Base { /* No virtual destructor */ };
class Derived : public Base { /* ... */ };
Base* ptr = new Derived();
delete ptr; // Undefined behavior: Derived's destructor is NOT called ❌
Best Practices
- Rule of Three/Five: If you define a custom destructor, follow the Rule of Three/Five (implement copy/move constructors/operators).
- Avoid Public Non-Virtual Destructors in Base Classes: This risks resource leaks.
By using virtual destructors in base classes, you ensure proper cleanup and prevent undefined behavior in polymorphic hierarchies.