Normal (Non-Virtual) Destructor
When you delete a derived object through a base class pointer without a virtual destructor, only the base class destructor is called, not the derived one.
Example:
Copy#include <iostream>
using namespace std;
class Base {
public:
~Base() { cout << "Base Destructor\n"; }
};
class Derived : public Base {
public:
~Derived() { cout << "Derived Destructor\n"; }
};
int main() {
Base* obj = new Derived(); // base pointer, derived object
delete obj; // only Base destructor called!
}
Output:
CopyBase Destructor
π΄ Problem:
The Derived destructor is never called. If Derived allocated resources (like heap memory, file handles, sockets), they would not be released, causing memory leaks.
Virtual Destructor
When you declare the destructor as virtual in the base class, C++ ensures that the destructor of the most derived object is called first (like how virtual functions work with dynamic dispatch).
Fixed Example:
Copy#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { cout << "Base Destructor\n"; }
};
class Derived : public Base {
public:
~Derived() { cout << "Derived Destructor\n"; }
};
int main() {
Base* obj = new Derived();
delete obj; // both destructors called
}
Output:
CopyDerived Destructor
Base Destructor
β Now cleanup is correct:
-
First
Deriveddestructor runs (releases derived resources). -
Then
Basedestructor runs (releases base resources).
3. Why Virtual Destructors Work (Internals)
-
A V-Table (Virtual Table) is created for classes with virtual methods (including destructors).
-
Each object stores a hidden pointer (
vptr) to its classβs V-Table. -
When you call
delete obj;, the runtime checks the vtable entry for destructor of the actual object type. -
This ensures the correct destructor chain (Derived β Base).
4. When to Use Virtual Destructors
-
If a class is intended to be inherited and will be deleted through a base class pointer β make destructor
virtual. -
If a class is not meant to be inherited β no need for virtual destructor (avoids vtable overhead).
5. Virtual Destructor vs Virtual Functions
-
Similar mechanism: Both use vtables for dynamic dispatch.
-
Destructor difference: Always called in chain order:
- Derived destructor β Base destructor (safe cleanup).
6. Special Cases
-
Pure Virtual Destructor
- You can even make a destructor pure virtual:
Copyclass Abstract { public: virtual ~Abstract() = 0; // pure virtual destructor }; Abstract::~Abstract() { cout << "Abstract Destructor\n"; }- Still requires a definition (because destructors always get called).