El destructor no virtual en la clase base, pero el destructor virtual en la clase derivada causa un error de segmentación

Recientemente, en una entrevista de trabajo, me preguntaron sobre el problema de la pérdida de memoria en las clases derivadas cuando el destructor de la clase base no se declara virtual.

Escribí una pequeña prueba para confirmar mi respuesta, pero encontré algo interesante. Obviamente, si creas unDerived objeto a través denew pero almacena su puntero comoBase*, no se llamará al destructor del objeto derivado si se elimina el puntero (tanto para mi respuesta a la pregunta).

Pensé si el destructor de la clase derivada es virtual o no es irrelevante en ese caso, pero en mi sistema el siguiente código muestra lo contrario:

#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;
}

Hubiera esperado que el programa produjera las siguientes dos líneas y salga normalmente:

Destructing: Base
Destructing: Base

Obtengo esa salida, pero inmediatamente después de la segunda línea, el programa se cierra con una falla de segmentación. Y el mensaje:

*** Error in `...': free(): invalid pointer: 0x00000000006020e8 ***

He cambiado el orden de las dos llamadas adelete, pero el programa siempre segregaría en la llamada adelete virtual_derived;. ¿Alguien puede decirme por qué esto es así?

Respuestas a la pregunta(2)

Su respuesta a la pregunta