缺少虚拟析构函数内存效果

时间:2015-07-27 21:59:56

标签: android c++ virtual

根据标准,缺少虚拟析构函数的多态性会导致未定义的行为。在实践中,它确实导致在删除父类时未调用派生类的析构函数。但是,它是否也导致任何常见编译器/系统中的内存泄漏?我对Android / Linux上的g++特别感兴趣。

具体来说,我指的是派生类的内存的删除是否会以某种方式泄露。考虑:

class Base {}
class Derived {
    int x;
}

如果我将Base*删除为Derived,我会泄漏4个字节吗?或者内存分配器是否已根据分配知道要释放多少字节?

3 个答案:

答案 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)如果要删除的对象的动态类型与其静态类型不同,则行为未定义。