Sign in to follow this  
guyaton

advantages of virtual destructors

Recommended Posts

guyaton    130
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

Share this post


Link to post
Share on other sites
snk_kid    1312
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.

Share this post


Link to post
Share on other sites
Enigma    1410
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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this