Pure Virtual Destructors in C++: Definition and Use

Pure virtual destructors in C++ provide a way to create an abstract base class while ensuring safe polymorphic resource cleanup. Unlike standard pure virtual functions, a pure virtual destructor must have an implementation because it is always invoked during the destruction sequence of derived classes. This guide explains how to declare, implement, and safely use pure virtual destructors in C++.

Purpose of a Pure Virtual Destructor

A class in C++ becomes abstract—meaning it cannot be directly instantiated—as soon as it contains at least one pure virtual function. In design scenarios where a class should be abstract but lacks a natural candidate for a pure virtual function, the destructor can be designated as pure virtual instead.

Making the destructor virtual guarantees that deleting a derived object via a base class pointer invokes the full chain of destructors without undefined behavior.

Declaring a Pure Virtual Destructor

A pure virtual destructor is declared within the class definition by combining the virtual keyword and the = 0 pure specifier:

class AbstractBase {
public:
    virtual ~AbstractBase() = 0;
};

Implementing the Pure Virtual Destructor

While pure virtual member functions usually do not require a function body, pure virtual destructors are an exception. When a derived class instance is destroyed, its destructor runs first, followed by the destructors of its base classes in reverse order of inheritance. If the base class destructor lacks a body, the linker will produce an unresolved external symbol error.

The definition must be provided outside the class declaration:

AbstractBase::~AbstractBase() {
    // Cleanup code, or leave empty if no resources to free
}

An inline definition inside the class declaration (e.g., virtual ~AbstractBase() = 0 {}) is not permitted by the C++ standard.

Complete Working Example

The following code illustrates declaration, implementation, inheritance, and dynamic memory management:

#include <iostream>

class Base {
public:
    // Pure virtual destructor declaration
    virtual ~Base() = 0;
};

// Pure virtual destructor implementation
Base::~Base() {
    std::cout << "Base destructor called\n";
}

class Derived : public Base {
public:
    ~Derived() override {
        std::cout << "Derived destructor called\n";
    }
};

int main() {
    // Base b; // Compile error: Base is an abstract class

    Base* ptr = new Derived();
    delete ptr; // Correctly calls Derived::~Derived(), then Base::~Base()

    return 0;
}

Output:

Derived destructor called
Base destructor called

Key Rules to Remember

  1. Mandatory Body: A pure virtual destructor must always have an implementation body outside the class; otherwise, linking will fail.
  2. Abstract Status: Adding = 0 to the destructor immediately makes the class abstract, preventing direct instantiation.
  3. Derived Class Overrides: A derived class does not need to explicitly declare a destructor unless it needs custom cleanup logic. The compiler will automatically generate a destructor that calls the pure virtual base destructor.
  4. Destruction Order: Inheritance cleanup always moves from the most derived class up to the root base class.