In C++, a pure virtual destructor is commonly used for abstract classes. Abstract classes serve as base classes for defining interfaces and partial implementations, and they cannot be directly instantiated. A pure virtual destructor is a destructor declared in a class without an implementation; its primary purpose is to ensure derived classes provide appropriate destructors for their objects.
Why Do We Need a Pure Virtual Destructor?
-
Enforces derived classes to implement their own destructor: A pure virtual destructor ensures that every derived class inheriting from the abstract class must implement its own destructor. This is necessary, especially when derived classes manage resources requiring special handling (such as dynamically allocated memory, file handles, network connections, etc.).
-
Enables safe deletion in polymorphism: If a class contains at least one pure virtual function, it is an abstract class and cannot be directly instantiated. In polymorphism, derived class objects are typically manipulated through base class pointers. When deleting a derived class object via a base class pointer, if the base class destructor is not virtual, only the base class destructor is invoked, not the derived class destructor. This may result in resources allocated in the derived class being improperly released, leading to memory leaks and other issues. Declaring the destructor as virtual ensures that when deleting an object via a base class pointer, the derived class destructor is correctly called.
Example Illustration:
Suppose we have an abstract base class Graphic for graphical objects, which contains pure virtual functions for drawing operations, and we want to ensure that any derived graphical objects can be properly destructed:
cppclass Graphic { public: virtual void draw() const = 0; // Pure virtual function virtual ~Graphic() = 0; // Pure virtual destructor }; Graphic::~Graphic() { // Although declared as pure virtual, an implementation can be provided. // This can include cleanup code for resources allocated in the base class. } class Circle : public Graphic { public: void draw() const override { // Implementation of drawing a circle } ~Circle() { // Cleanup Circle-specific resources, such as dynamically allocated memory } };
In this example, the Circle class inherits from Graphic. Since Graphic contains a pure virtual destructor, all derived classes (such as Circle) must implement their own destructor. Thus, whenever a Circle object is deleted via a Graphic pointer, the Circle destructor is called first, followed by the Graphic destructor, safely cleaning up all resources.