Sebastian Schneider
Sebastian Schneider

Reputation: 123

Non virtual destructor in base class, but virtual destructor in derived class cause segmentation fault

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

Answers (2)

satyag
satyag

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

Crowman
Crowman

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

Related Questions