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

