根据标准,缺少虚拟析构函数的多态性会导致未定义的行为。在实践中,它确实导致在删除父类时未调用派生类的析构函数。但是,它是否也导致任何常见编译器/系统中的内存泄漏?我对Android / Linux上的g++
特别感兴趣。
具体来说,我指的是派生类的内存的删除是否会以某种方式泄露。考虑:
class Base {}
class Derived {
int x;
}
如果我将Base*
删除为Derived
,我会泄漏4个字节吗?或者内存分配器是否已根据分配知道要释放多少字节?
答案 0 :(得分:3)
当然可以做到。考虑:
class A
{
public:
virtual void func() {}
};
class B : public A
{
public:
void func() { s = "Some Long String xxxxxx"; }
private:
std::string s;
// destructor of B will call `std::string` destructor.
};
A* func(bool b)
{
if (b)
return new B;
return new A;
}
...
A* a = func(true);
...
delete a;
现在,这会造成内存泄漏,因为std::string s
对象中的B
未被A::~A
释放 - 您需要调用B::~B
,这只会发生如果析构函数是虚拟的。
请注意,这适用于我所知道的所有编译器和所有运行时系统(这些都是常见的,有些则不常见)。
修改:
基于更新的实际问题:内存取消分配基于分配的大小发生,因此如果您可以保证由于类的构造/使用而不会发生单个分配,那么不安全一个虚拟的析构函数。然而,如果基类的“客户”可以创建他/她自己的扩展类,这会导致有趣的问题。将派生类标记为final
将防止它们被进一步派生,但如果基类在其他人可以包含的头文件中可见,那么您冒着从{{1}派生自己的类的风险做分配的东西。
所以,换句话说,就像Base
这样的东西,PImpl
类隐藏在源文件中,没有其他人从中派生出来,这样做是合理的。对于大多数其他情况,可能是一个坏主意。
答案 1 :(得分:0)
缺少析构函数会导致未定义的行为,因为编译器确切地知道副作用可能是不可信的。
将其视为RAII的清理方面。在这种情况下,如果你设法不清理尽管声称你做了,副作用可能是:
答案 2 :(得分:0)
这应该导致未定义的行为,这意味着它也可能导致内存泄漏。在delete
的5.3.5 / 3(n4296 c ++ 14)中,你有:
在第一个替代(删除对象)中,如果要删除的对象的静态类型与其不同 动态类型,静态类型应该是要删除的对象的动态类型的基类 static类型应具有虚拟析构函数或行为未定义。在第二种选择中(删除 array)如果要删除的对象的动态类型与其静态类型不同,则行为未定义。