liveBook · Manning

Destruction order

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

Destruction order is the language-defined ordering in which class subobjects and their destructors are processed when an object is destroyed.

Destruction order runs opposite to construction order: the most-derived class is destroyed first, followed by each base class up to the most-base class. This preserves the assumptions derived classes make about their bases while their own state is still available. section 8.8

A common mistake is calling virtual functions from a destructor. Destruction may already have invalidated the most-derived state that the virtual function expects, so once destruction begins, avoid virtual calls. section 8.9

Use destruction order safely by following these rules:

  • Give a polymorphic base class a virtual destructor whenever it declares virtual functions. section 8.8
  • Destroy derived objects through pointers or references only when the base interface supports polymorphic destruction. section 8.8
  • Avoid virtual calls in constructors and destructors; call behavior after construction has finished instead. section 8.9
  • Ensure classes that own dynamic resources release them in their destructors, including resources such as heap memory and system handles. section 8.8

Deleting through a base pointer

A Shape pointer can refer to a Square. With a non-virtual Shape destructor, deleting that pointer prints only "~Shape" and leaves the Square-owned resource unreleased. Making Shape's destructor virtual changes the sequence to "~Square" followed by "~Shape", so derived cleanup occurs before base cleanup. 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;
} 

Inspect the complete base-and-derived example showing the virtual destructor correction. section 8.8

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

How deep it goes
Explains derived-before-base ordering, virtual destructor requirements, and virtual-call hazards through executable examples.
Examples
Uses Person, Student, and GradStudent construction examples plus Shape and Square destructor output to expose lifecycle errors.
What it assumes
Assumes familiarity with classes and inheritance while teaching polymorphic destruction and lifecycle ordering through concrete code.
What you can do after
Diagnose/debug with it by identifying missing virtual destructors, unsafe lifecycle virtual calls, and incomplete derived-resource cleanup.
How it connects
Connects destruction order with inheritance, virtual functions, virtual destructors, polymorphism, class invariants, and resource management.

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.