虚拟析构函数如何工作?

时间:2010-04-27 15:28:35

标签: c++ visual-c++ destructor bounds-checker

几个小时后,我正在摆弄一个内存泄漏问题,事实证明我真的有一些关于虚拟析构函数的基本内容错了!让我解释一下我的班级设计。

class Base
{
  virtual push_elements()
  {}
};

class Derived:public Base
{
vector<int> x;
public:
   void push_elements(){ 
      for(int i=0;i <5;i++)
         x.push_back(i); 
   }
};

void main()
{
    Base* b = new Derived();
    b->push_elements();
    delete b;
}

bounds checker工具报告派生类向量中的内存泄漏。我发现析构函数不是虚拟的,并且不会调用派生类析构函数。当我将析构函数虚拟化时,令人惊讶地得到了解决。即使未调用派生类析构函数,也不会自动释放向量吗?这是BoundsChecker工具中的怪癖还是我对虚拟析构函数的理解错误?

8 个答案:

答案 0 :(得分:15)

当基类没有虚析构函数时,通过基类指针删除派生类对象会导致未定义的行为。

您所观察到的(对象的派生类部分永远不会被破坏,因此其成员永远不会被解除分配)可能是许多可能行为中最常见的,并且是确保为什么重要的一个很好的例子当你以这种方式使用多态时,你的析构函数是虚拟的。

答案 1 :(得分:8)

如果基类没有虚拟析构函数,那么代码的结果是未定义的行为,不一定是被调用的错误析构函数。这可能是BoundsChecker正在诊断的内容。

答案 2 :(得分:2)

虽然这在技术上是未定义的,但您仍需要知道最常见的故障方法才能进行诊断。常见的失败方法是调用错误的析构函数。我不知道任何其他方式会失败的实现,但不可否认我只使用了两个实现。

发生这种情况的原因与当您尝试覆盖非虚拟成员函数并通过基指针调用它时调用'错误'函数的原因相同。

答案 3 :(得分:1)

如果析构函数不是虚拟的,那么将调用Base析构函数。基础析构函数清理Base对象并完成。基础对象析构函数无法知道派生对象,它必须是被调用的派生析构函数,与任何函数一样,这样做的方法是使析构函数成为虚函数。

答案 4 :(得分:1)

来自C ++ FAQ Lite:“我的析构函数什么时候应该是虚拟的?”阅读here。 (顺便说一句,C ++ FAQ Lite是您解决与C ++相关的所有问题的绝佳来源。)

答案 5 :(得分:1)

在C ++中,普通的析构函数是一个递归定义的概念 - 当类的每个成员(以及每个基类)都有一个简单的析构函数时,它就是编译器为你编写的析构函数。 (有一个类似的概念叫做琐碎的构造函数。)

当一个具有重要析构函数的对象包含在一个对象中时(如示例中的vector),那么外部对象的析构函数(如Derived)in不再是微不足道的。即使您没有编写析构函数,C ++编译器也会自动编写一个析构函数来调用任何具有析构函数的成员的析构函数。

所以,即使你没有写任何东西,编写非虚拟析构函数的注意事项仍然适用。

答案 6 :(得分:1)

如果你来自c#,那么你就是在想知道为什么矢量没有被自动解除分配。但是在c ++中,除非您使用Microsoft Manged Extesions to C ++(C ++ / CLI),否则无法使用自动内存管理。

因为Base类中没有析构函数是虚拟的,所以派生类对象永远不会被释放,因此你泄漏了为派生类的向量数据成员分配的内存。

答案 7 :(得分:0)

析构函数是类的成员函数,其名称与类名相同,并以波形符号(〜)开头。当对象超出范围时,析构函数用于销毁类的对象,或者您可以说在析构函数中完成所有清除类的破坏。在对象超出范围时,在类中构造对象期间分配的所有内存都会被破坏(或释放内存)。

使用BoundsCheck

上的示例查找更多详细信息