A destructor is a C++ special member function that runs when an object is destroyed. It releases owned resources, such as dynamic memory, file handles, sockets, or database connections, and preserves the class invariant. section 7.6 section 10.0
Avoid these practical destructor mistakes:
- Failing to release an owned dynamic resource leaks it when the object leaves scope. section 7.6
- Deleting a derived object through a base pointer without a virtual destructor creates undefined behavior and can skip derived cleanup. section 8.8
- Combining RAII ownership with manual deletion of the same pointer can cause a double free. section 10.5
- Calling exit bypasses stack unwinding, so destructors do not run and acquired resources can leak. section 5.9
A destructor belongs with the copy constructor and copy-assignment operator when a class directly manages dynamic or limited resources. Copying must preserve ownership rules rather than merely copying pointer values. Modern ownership types can reduce the need for handwritten cleanup, but copying and assignment still require deliberate semantics. section 7.6 section 10.5
Polymorphic deletion
A Shape owns a Point and declares a virtual area function. If Shape has an ordinary destructor, deleting a Square through Shape* prints only ~Shape; Square's allocated side is not cleaned up. Declaring virtual ~Shape() makes deletion run ~Square followed by ~Shape. 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 base-class destructor with the polymorphic deletion example and observe the destruction order. section 8.8
RAII makes destruction the automatic cleanup point: acquire the resource during construction, transfer ownership clearly, and let scope exit handle normal and exceptional paths. Test both paths, especially when maintaining legacy code. section 10.5 section 5.9


