Reputation: 123
recently in a job interview I was asked about the problem of leaking memory in derived classes when the base class's destructor is not declared virtual.
I wrote a small test to confirm my answer, but I found something interesting. Obviously, if you create a Derived
object via new
but store its pointer as a Base*
, the derived object's destructor won't be called, if the pointer is deleted (so much for my answer to the question).
I thought whether the derived class's destructor is virtual or not is irelevant in that case, but on my system the following code shows otherwise:
#include <iostream>
#include <string>
// just a helper class, printing its name out when it is destructed
class PrintOnDestruct
{
public:
PrintOnDestruct( const std::string& name )
: name_( name )
{}
~PrintOnDestruct()
{
std::cout << "Destructing: " << name_ << std::endl;
}
protected:
std::string name_;
};
// the Base class
class Base
{
public:
Base()
{
print_on_destruct_ = new PrintOnDestruct( "Base" );
}
// the destructor is NOT virtual!
~Base()
{
delete print_on_destruct_;
}
protected:
PrintOnDestruct* print_on_destruct_;
};
// the NonVirtualDerived class, doesn't have a virtual destructor either
class NonVirtualDerived : public Base
{
public:
NonVirtualDerived()
: Base()
{
print_on_destruct_child_ = new PrintOnDestruct( "NonVirtualDerived" );
}
// the destructor is NOT virtual!
~NonVirtualDerived()
{
delete print_on_destruct_child_;
}
protected:
PrintOnDestruct* print_on_destruct_child_;
};
// the VirtualDerived class does have a virtual destructor
class VirtualDerived : public Base
{
public:
VirtualDerived()
: Base()
{
print_on_destruct_child_ = new PrintOnDestruct( "VirtualDerived" );
}
// the destructor is virtual!
virtual ~VirtualDerived()
{
delete print_on_destruct_child_;
}
protected:
PrintOnDestruct* print_on_destruct_child_;
};
int main()
{
// create the two child classes
Base* non_virtual_derived = new NonVirtualDerived;
Base* virtual_derived = new VirtualDerived;
// delete the two objects
delete non_virtual_derived; // works as expected (only calls Base's destructor, the memory of NonVirtualDerived will be leaked)
delete virtual_derived; // segfault, after calling Base's destructor
return 0;
}
I would have expected the program to output the following two lines and quit normally:
Destructing: Base
Destructing: Base
I get that output, but immediately after the second line the program quits with a segmentation fault. And the message:
*** Error in `...': free(): invalid pointer: 0x00000000006020e8 ***
I've changed the order of the two calls to delete
, but the programm would always segfault in the call to delete virtual_derived;
. Can anybody tell me why this is so?
Upvotes: 8
Views: 2647
Reputation: 96
The answer really lies in the statement:
Base* virtual_derived = new VirtualDerived;
You are trying to 'free' an address that was not returned by 'malloc'. To understand why, replace this line with
VirtualDerived* x = new VirtualDerived;
Base* virtual_derived = x;
If you print these two addresses, you will notice that 'x' and 'virtual_derived' have different values. The address that 'malloc' returned (via 'new') is 'x' and the address that was passed to 'free' (via 'delete') is 'virtual_derived'.
Upvotes: 8
Reputation: 25936
You have to declare the destructor in the base class as virtual
, which you do not do in this example. Declaring it as virtual
in the derived class only is not sufficient.
Upvotes: 2