advantages of virtual destructors

Started by
2 comments, last by guyaton 18 years, 9 months ago
After reading a bunch of the posts, I noticed that there was mention of declaring class destructors as virtual. I was wondering what the advantage of doing this does. If you have a class lets say class 'a', that inherits class 'b', when class 'a' is deleted isn't the destructor for class 'b' also called? I just need some clarification. thanks everyone in advance. ~guyaton
~guyaton
Advertisement
The time when you need a virtual destructor is when you intend on invoking delete on pointers to base classes, if it is not virtual then its undefined behaviour as to what happens in the standard (an imp detail) that is a place you do not want to be in [grin], the most likely outcome is to cause resource leaks.
delete([])ing a derived instance via a pointer-to-base where the base class does not have a virtual destructor is undefined behaviour. Consider the following:
#include <iostream>class Base{	public:		~Base()		{			std::cout << "~Base\n";		}		virtual void virtualFunction()		{			std::cout << "Base::virtualFunction\n";		}		void nonVirtualFunction()		{			std::cout << "Base::nonVirtualFunction\n";		}};class Derived	:	public Base{	public:		~Derived()		{			std::cout << "~Derived\n";		}		virtual void virtualFunction()		{			std::cout << "Derived::virtualFunction\n";		}		void nonVirtualFunction()		{			std::cout << "Derived::nonVirtualFunction\n";		}};int main(){	Base base;	Derived derived;	Base * basePointerToBase = new Base;	Derived * derivedPointerToDerived = new Derived;	Base * basePointerToDerived = new Derived;	base.virtualFunction(); // [1]	base.nonVirtualFunction(); // [2]	derived.virtualFunction(); // [3]	derived.nonVirtualFunction(); // [4]	basePointerToBase->virtualFunction(); // [5]	basePointerToBase->nonVirtualFunction(); // [6]	derivedPointerToDerived->virtualFunction(); // [7]	derivedPointerToDerived->nonVirtualFunction(); // [8]	basePointerToDerived->virtualFunction(); // [9]	basePointerToDerived->nonVirtualFunction(); // [10]	delete basePointerToDerived; // [11]	delete derivedPointerToDerived; // [12]	delete basePointerToBase; // [13]	// derived.~Derived(); // [14]	// base.~Base(); // [15]}

What do the calls 1-15 do? Well, first, we know that any call via an actual instance will automatically resolve on the static type of the instance because the instance can have no other type. So calls 1-4 resolve as:

base.virtualFunction(); // calls Base::virtualFunction() [1]
base.nonVirtualFunction(); // calls Base::nonVirtualFunction() [2]
derived.virtualFunction(); // calls Derived::virtualFunction() [3]
derived.nonVirtualFunction(); // calls Derived::nonVirtualFunction() [4]

Calls 14 & 15 are handled similarly, with the exception that a derived class destructs it's base class part after it destructs itself:

// derived.~Derived(); // calls Derived::~Derived(), which calls Base::~Base() [14]
// base.~Base(); // calls Base::~Base() [15]

Next we have pointers. For non-virtual functions a call via a pointer is still resolved to the static type of the pointer, so calls 6, 8 & 10 resolve as:

basePointerToBase->nonVirtualFunction(); // calls Base::nonVirtualFunction() [6]
derivedPointerToDerived->nonVirtualFunction(); // calls Derived::nonVirtualFunction() [8]
basePointerToDerived->nonVirtualFunction(); // calls Base::nonVirtualFunction() [10]

For virtual functions the call instead resolved to the dynamic type of the pointer, so calls 5, 7 & 9 resolve as:

basePointerToBase->virtualFunction(); // calls Base::virtualFunction() [5]
derivedPointerToDerived->virtualFunction(); // calls Derived::virtualFunction() [7]
basePointerToDerived->virtualFunction(); // calls Derived::virtualFunction() [9]

Now this leads us to the the destructors. A non-virtual destructor resolves to the static type of the pointer, so calls 11 to 13 resolve to:

delete basePointerToBase; // calls Base::~Base() [11]
delete derivedPointerToDerived; // calls Derived::~Derived(), which calls Base::~Base() [12]
delete basePointerToDerived; // calls Base::~Base() - uhoh! What happened to the Derived part of the object? [13]

Here the error is made obvious. The Derived part of the object pointed at by basePointerToDerived is never destructed.
If the destructor is made virtual the it correctly resolves to the dynamic type of the object, just like for other virtual functions, making calls 11 to 13 resolve to:

delete basePointerToBase; // calls Base::~Base() [11]
delete derivedPointerToDerived; // calls Derived::~Derived(), which calls Base::~Base() [12]
delete basePointerToDerived; // calls Derived::~Derived(), which calls Base::~Base() [13]

As they need to.

Enigma
wow. thats a pretty amazing sample. spells it out nicely. thanks alot!

~guyaton
~guyaton

This topic is closed to new replies.

Advertisement