Monday 16 April 2012

When does pure virtual destructor need its definition?

Deleting an instance of inherited class through a pointer to the base class yields memory leaks if destructor of the base class is not declared as virtual:

main.cpp:


Output:
main()
Parent::Parent()
Child::Child()
Child::foo()

Windows debugger output:
Detected memory leaks!
Dumping objects ->
{142} normal block at 0x00315588, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

It is not enough just to declare base class as virtual, but we need to provide its definition as well (the one with empty body will suffice), otherwise linker will report error (if derived class' destructor is defined, implicitly or explicitly, as it calls base class destructor):

error LNK2001: unresolved external symbol "public: virtual __thiscall Parent::~Parent(void)" (??1Parent@@UAE@XZ)

Note that if our code didn't call (implicitly or explicitly) Child's destructor, and so Parent's one, linker would not complain. But in our case we have construction and destruction of Child object so Parent needs to have destructor defined:



Output (memory leaks are not reported anymore):
main()
Parent::Parent()
Child::Child()
Child::foo()
Child::~Child()
Parent::~Parent()

We are able to instantiate Parent class but if we want to prevent that but with keeping default implementation of foo(), we can declare its destructor as pure virtual function. But there is one interesting thing: although it is declared as pure virtual, we still need to provide implementation of destructor, otherwise linker will complain (if derived class' destructor is defined - which calls base class destructor)!

Parent as abstract base class:


Just to make this article complete, it is worth mentioning that if foo() wasn't declared as virtual in the base class, we would have needed to cast base class pointer to derived class pointer in order to invoke derived class' version of the function:



Output (no memory leaks):
main()
Parent::Parent()
Child::Child()
Parent::foo()
Child::foo()
Child::~Child()
Parent::~Parent()

Further reading:
(Im)pure Virtual Functions