liveBook · Manning

Virtual destructor

As explained in 100 C++ Mistakes and How to Avoid Them

A virtual destructor is a destructor declared virtual so destruction through a base-class interface dispatches to the derived object's destructor.

A virtual destructor lets destruction through a base-class pointer dispatch to the derived object's destructor. Without it, deleting a derived object through its base pointer has undefined behavior and may leak resources. section 8.8

Mistake: deleting through a nonvirtual base destructor

Shape has a virtual area function but a nonvirtual destructor. After Shape* s points to a dynamically allocated Square, delete s calls only ~Shape; ~Square is skipped, so Square's dynamic resource leaks. section 8.8

Listing 8.15 Base class without a virtual destructor
class Point {
private:
    double x;
    double y;
public:
    Point(double x, double y) : x(x), y(y) {}
};
 
class Shape {
private:
    Point* location;
public:
    Shape(double x, double y) : location(new Point(x, y)) {}
    ~Shape() { delete location;  std::cout << "~Shape\n"; }    #1
    virtual double area() const = 0;                           #2
};
 
class Square : public Shape {
private:
    double* side;
public:
    Square(double side, double x, double y) : Shape(x, y), 
        side(new double(side)) {}
    ~Square() { delete side;  std::cout << "~Square\n"; }
    double area() const { return *side * *side; }  // looks weird
};
 
int main() {
    Shape* s = new Square(2.5, 0, 0);  // place at origin
    std::cout << s->area() << '\n';
    delete s;                                                  #3
    return 0;
}

Inspect the failing Shape and Square example, including the base-pointer deletion that produces only 6.25 and ~Shape. section 8.8

Declare the base destructor virtual when the class is intended for inheritance and already has virtual behavior. The corrected version prints ~Square before ~Shape, allowing both subobjects to clean up. section 8.8

Listing 8.16 A base class with virtual destructor
class Point {
private:
    double x;
    double y;
public:
    Point(double x, double y) : x(x), y(y) {}
};
 
class Shape {
private:
    Point* location;
public:
    Shape(double x, double y) : location(new Point(x, y)) {}
    virtual ~Shape() { delete location;                     #1
        std::cout << "~Shape\n"; }    
    virtual double area() const = 0;
};
 
class Square : public Shape {
private:
    double* side;
public:
    Square(double side, double x, double y) : Shape(x, y),
        side(new double(side)) {}
    ~Square() { delete side;  std::cout << "~Square\n"; }
    double area() const { return *side * *side; }  // still looks weird
};
 
int main() {
    Shape* s = new Square(2.5, 0, 0);  // place at origin
    std::cout << s->area() << '\n';
    delete s;
    return 0;
} 

Compare the corrected declaration and destruction order in the version with virtual ~Shape(). section 8.8

Polymorphic containers also require careful ownership and element choices:

  • Use pointer elements when a container must hold base and derived instances without slicing; ensure the base class has a virtual destructor when elements are copied or deleted. section 6.7
  • Prefer modern C++ ownership improvements when available; raw-pointer guidance applies to premodern C++ constraints. section 6.7

How 100 C++ Mistakes and How to Avoid Them covers it

How deep it goes
Explains the rule, demonstrates undefined behavior and leakage, and contrasts failing and corrected destruction output.
Examples
Uses Shape and Square resource cleanup plus Person and Employee objects in polymorphic containers.
What it assumes
Assumes familiarity with inheritance, virtual functions, pointers, dynamic allocation, and destructors.
What you can do after
Diagnose/debug leaks from base-pointer deletion and correct polymorphic base classes with virtual destructors.
How it connects
Connects inheritance, polymorphism, base classes, derived classes, pointers, containers, slicing, and memory leaks.

Also explained in

Compare how 2 books explain virtual destructor →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 C++ Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.