Nie wirtualny destruktor w klasie bazowej, ale wirtualny destruktor w klasie pochodnej powoduje błąd segmentacji

ostatnio w rozmowie kwalifikacyjnej zapytano mnie o problem wycieku pamięci w klasach pochodnych, gdy destruktor klasy bazowej nie jest zadeklarowany jako wirtualny.

Napisałem mały test, aby potwierdzić moją odpowiedź, ale znalazłem coś interesującego. Oczywiście, jeśli stworzyszDerived obiekt przeznew ale przechowuj jego wskaźnik jakoBase*, destruktor obiektu pochodnego nie zostanie wywołany, jeśli wskaźnik zostanie usunięty (tyle, jeśli chodzi o moją odpowiedź na pytanie).

Pomyślałem, że destruktor klasy pochodnej jest wirtualny lub nie ma znaczenia w tym przypadku, ale w moim systemie poniższy kod pokazuje inaczej:

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

Spodziewałbym się, że program wyśle ​​dwa następujące wiersze i zakończy normalnie:

Destructing: Base
Destructing: Base

Dostaję to wyjście, ale zaraz po drugiej linii program kończy pracę z błędem segmentacji. A wiadomość:

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

Zmieniłem kolejność dwóch połączeńdelete, ale program zawsze będzie się segregował podczas połączeniadelete virtual_derived;. Czy ktoś może mi powiedzieć, dlaczego tak jest?

questionAnswers(2)

yourAnswerToTheQuestion