【发布时间】:2013-11-01 18:13:58
【问题描述】:
最近在一次求职面试中,有人问我当基类的析构函数未声明为虚拟时,派生类中的内存泄漏问题。
我写了一个小测试来确认我的答案,但我发现了一些有趣的东西。显然,如果您通过new 创建了Derived 对象,但将其指针存储为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; 时总是会出现段错误。谁能告诉我为什么会这样?
【问题讨论】:
-
尝试更改 Base* 分配以使用 static_cast 而不是隐式强制转换,看看是否有任何变化。可能不会,但在这种情况下最好还是明确一点。
-
通过指向具有非虚拟析构函数的基类的指针进行删除只是未定义的行为。
-
@hyde 使用 static_cast 不会改变任何东西。
-
@SebastianSchneider 是的,隐式转换应该等于显式
static_cast。尽管事后看来,正如当前最佳答案中所解释的那样,需要使用虚拟方法(或多重继承)进行强制转换(隐式或显式)表明指针值可能因类型而异。
标签: c++ inheritance polymorphism destructor