Pure virtual destructors
While
pure
virtual destructors are legal in Standard C++, there is an added constraint when
using them: you must provide a function body for the pure virtual destructor.
This seems counterintuitive; how can a virtual function be “pure”
if it needs a function body? But if you keep in mind that constructors and
destructors are special operations it makes more sense, especially if you
remember that all destructors in a class hierarchy are always called. If you
could leave off the definition for a pure virtual destructor, what
function body would be called during destruction? Thus, it’s absolutely
necessary that the compiler and linker enforce the existence of a function body
for a pure virtual destructor.
If it’s pure, but it has to have a
function body, what’s the value of it? The only difference you’ll
see between the pure and non-pure virtual destructor is that the pure virtual
destructor does cause the base class to be abstract, so you cannot create an
object of the base class (although this would also be true if any other member
function of the base class were pure virtual).
Things are a bit confusing, however, when
you inherit a class from one that contains a pure virtual destructor. Unlike
every other pure virtual function, you are not required to provide a
definition of a pure virtual destructor in the derived class. The fact that the
following compiles and links is the proof:
//: C15:UnAbstract.cpp
// Pure virtual destructors
// seem to behave strangely
class AbstractBase {
public:
virtual ~AbstractBase() = 0;
};
AbstractBase::~AbstractBase() {}
class Derived : public AbstractBase {};
// No overriding of destructor necessary?
int main() { Derived d; } ///:~
Normally, a pure virtual function in a
base class would cause the derived class to be abstract unless it (and all other
pure virtual functions) is given a definition. But here, this seems not to be
the case. However, remember that the compiler automatically creates a
destructor definition for every class if you don’t create one.
That’s what’s happening here – the base class destructor is
being quietly overridden, and thus the definition is being provided by the
compiler and Derived is not actually abstract.
This brings up an interesting question:
What is the point of a pure virtual destructor? Unlike an ordinary pure virtual
function, you must give it a function body. In a derived class, you
aren’t forced to provide a definition since the compiler synthesizes the
destructor for you. So what’s the difference between a regular virtual
destructor and a pure virtual destructor?
The only distinction occurs when you have
a class that only has a single pure virtual function: the destructor. In this
case, the only effect of the purity of the destructor is to prevent the
instantiation of the base class. If there were any other pure virtual functions,
they would prevent the instantiation of the base class, but if there are no
others, then the pure virtual destructor will do it. So, while the addition of a
virtual destructor is essential, whether it’s pure or not isn’t so
important.
When you run the following example, you
can see that the pure virtual function body is called after the derived class
version, just as with any other destructor:
//: C15:PureVirtualDestructors.cpp
// Pure virtual destructors
// require a function body
#include <iostream>
using namespace std;
class Pet {
public:
virtual ~Pet() = 0;
};
Pet::~Pet() {
cout << "~Pet()" << endl;
}
class Dog : public Pet {
public:
~Dog() {
cout << "~Dog()" << endl;
}
};
int main() {
Pet* p = new Dog; // Upcast
delete p; // Virtual destructor call
} ///:~
As a guideline, any time you have a
virtual function in a class, you should immediately add a virtual destructor
(even if it does nothing). This way, you ensure against any surprises
later.