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
