When to use virtual destructors in C++?
A virtual destructor ensures that if you delete a derived object via a pointer to its base class, the derived class’s destructor is called first, followed by the base class destructor. This prevents resource leaks and other undefined behaviors.
1. Polymorphic Base Classes
Any class intended to be used polymorphically must have a virtual destructor. Specifically, if a class has at least one virtual function (such as a virtual void foo()
method), it often implies that you might store objects of derived types using a base class pointer. Without a virtual destructor in the base class, deleting a derived object via a base pointer would invoke only the base class destructor, causing partial destruction and leading to memory/resource leaks.
Example:
class Base { public: virtual ~Base() {} // Virtual destructor virtual void doSomething() {} }; class Derived : public Base { public: ~Derived() { // Free any resources specific to Derived } void doSomething() override { // Implementation } }; int main() { Base* obj = new Derived(); delete obj; // Correctly calls Derived's destructor, then Base's destructor return 0; }
2. Prevention of Undefined Behavior
- If the base destructor is not virtual and you call
delete
on aBase*
that actually points to aDerived
object, only the base destructor is called. - The derived object’s resources remain unfreed or partially unfreed—an undefined behavior scenario.
3. Cost of Virtual Destructors
- Memory: A virtual destructor adds a virtual function pointer (vtable) if none already exists. However, if your base class already has other virtual methods, then the overhead of making the destructor virtual is negligible.
- Runtime: The overhead of dynamic dispatch to the destructor is typically minimal.
In other words, it’s better to pay a small overhead than risk undefined behavior in a polymorphic system.
4. Exceptions
- If a class is not intended for polymorphic usage (i.e., no methods are declared virtual, and you never delete derived objects through a base pointer), you can safely omit a virtual destructor. In that case, the destructor can be non-virtual, potentially saving that tiny overhead.
5. Use Cases and Guidelines
- Any Polymorphic Base
- A base class that might be inherited and accessed through pointers.
- Example:
class Shape
with derived classes likeCircle
orSquare
.
- Non-Polymorphic Classes
- If you never plan to use them through base pointers (no virtual methods, no dynamic polymorphism), you can omit the virtual destructor.
- Interface-Only Classes
- Pure abstract classes used strictly as interfaces usually have a public virtual destructor (often defaulted) to ensure correct cleanup.
Why This Matters in Coding Interviews
Virtual destructors are fundamental to object-oriented programming in C++. Interviewers often ask about destructors, inheritance, and polymorphism to test understanding of memory management and resource lifetime—key areas for writing robust, bug-free C++ code.
If you’re preparing for software engineering interviews, here are a couple of resources from DesignGurus.io that may help level up your skills:
- Grokking the Coding Interview: Patterns for Coding Questions
This course helps you master the recurring coding patterns that frequently show up in technical interviews. - Grokking System Design Fundamentals
If you’re new to system design, this is a great place to learn how large-scale distributed systems are structured and designed.
For personalized feedback from experienced interviewers, consider booking a Coding Mock Interview session or check out their Interview Bootcamp for structured preparation.
Key Takeaway: Always use a virtual destructor for any base class that you intend to use polymorphically. This ensures correct destruction of derived objects and prevents memory leaks—an essential best practice for safe and effective C++ development.