8

最近在一次工作面试中,我被问到当基类的析构函数未声明为虚拟时,派生类中的内存泄漏问题。

我写了一个小测试来确认我的答案,但我发现了一些有趣的东西。显然,如果您通过创建Derived对象new但将其指针存储为 a Base*,则不会调用派生对象的析构函数,如果指针被删除(我对问题的回答就这么多)。

我认为在这种情况下派生类的析构函数是否是虚拟的无关紧要,但是在我的系统上,以下代码显示了其他情况:

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

我本来希望程序输出以下两行并正常退出:

Destructing: Base
Destructing: Base

我得到了那个输出,但是在第二行之后程序立即退出并出现分段错误。和消息:

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

我已将两次调用的顺序更改为delete,但程序总是会在调用delete virtual_derived;. 谁能告诉我为什么会这样?

4

2 回答 2

8

答案就在这句话中:

    Base* virtual_derived = new VirtualDerived;

您正在尝试“释放”一个未被“malloc”返回的地址。要了解原因,请将此行替换为

    VirtualDerived* x = new VirtualDerived;
    Base* virtual_derived = x;

如果您打印这两个地址,您会注意到 'x' 和 'virtual_derived' 具有不同的值。“malloc”返回的地址(通过“new”)是“x”,传递给“free”的地址(通过“delete”)是“virtual_derived”。

于 2013-10-23T13:13:33.133 回答
2

您必须将基类中的析构函数声明为virtual,在本示例中您没有这样做。仅在派生类中声明它virtual是不够的。

于 2013-10-23T12:58:07.723 回答