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?
Deleting a derived class object using a pointer of base class type that has a non-virtual destructor results in undefined behavior.
If you want to call a derived class function when all you have is a base class pointer/reference, that function must be declared virtual .
No. You never need to explicitly call a destructor (except with placement new). A derived class's destructor (whether or not you explicitly define one) automagically invokes the destructors for base class subobjects. Base classes are destructed after member objects.
Virtual destructors in C++ are used to avoid memory leaks especially when your class contains unmanaged code, i.e., contains pointers or object handles to files, databases or other external objects.
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'.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With