Nicht virtueller Destruktor in Basisklasse, aber virtueller Destruktor in abgeleiteter Klasse verursacht Segmentierungsfehler

Kürzlich wurde ich in einem Vorstellungsgespräch nach dem Problem des Speicherverlusts in abgeleiteten Klassen gefragt, wenn der Destruktor der Basisklasse nicht als virtuell deklariert ist.

Ich schrieb einen kleinen Test, um meine Antwort zu bestätigen, fand aber etwas Interessantes. Offensichtlich, wenn Sie eine erstellenDerived Objekt übernew aber speichern Sie den Zeiger alsBase*wird der Destruktor des abgeleiteten Objekts nicht aufgerufen, wenn der Zeiger gelöscht wird (soviel zu meiner Antwort auf die Frage).

Ich dachte, ob der Destruktor der abgeleiteten Klasse virtuell ist oder nicht, ist in diesem Fall irrelevant, aber auf meinem System zeigt der folgende Code etwas anderes:

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

Ich hätte erwartet, dass das Programm die folgenden zwei Zeilen ausgibt und normal beendet:

Destructing: Base
Destructing: Base

Ich bekomme diese Ausgabe, aber sofort nach der zweiten Zeile wird das Programm mit einem Segmentierungsfehler beendet. Und die Nachricht:

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

Ich habe die Reihenfolge der beiden Anrufe auf geändertdelete, aber das Programm würde immer im Aufruf von segfaultdelete virtual_derived;. Kann mir jemand sagen, warum das so ist?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage