Skip to main content

Virtual Destructors

intermediate15 min readLesson 85 of 204

Deleting through a base pointer without one is UB — the one-line rule and its cost.

Deleting a derived object through a base pointer without a virtual destructor is undefined behavior — typically the derived destructor never runs and resources leak.

#include <iostream>

class BaseBad {
public:
    ~BaseBad() { std::cout << "BaseBad dtor\n"; }       // NOT virtual
};

class DerivedBad : public BaseBad {
public:
    ~DerivedBad() { std::cout << "DerivedBad dtor\n"; } // never runs via Base*
};

class BaseGood {
public:
    virtual ~BaseGood() = default;                       // virtual: correct chain
};

class DerivedGood : public BaseGood {
public:
    ~DerivedGood() { std::cout << "DerivedGood dtor\n"; }
};

The rule, stated once

Any class intended for polymorphic deletion must have a public virtual destructor. If a type is not meant to be a base, keep its destructor non-virtual (and preferably make the class final). The cost of virtual ~T() = default is one vtable pointer; the cost of forgetting it is leaks and undefined behavior.

When you will not be deleted via base*

If ownership never travels through a base pointer — pure value semantics, or std::unique_ptr<Derived> with the deleter typed — a virtual destructor is not required. But interfaces leak less when you simply follow the rule: design a base → virtual ~Base() = default;, no exceptions.