虚析构函数是如何工作的?

5

我正在使用gcc。我知道虚析构函数如何解决当我们销毁由基类指针指向的派生类对象时的问题。我想知道它们是如何工作的?

class A
{
      public:
      A(){cout<<"A constructor"<<endl;}
     ~A(){cout<<"A destructor"<<endl;}

};

class B:public A
{
      public:
      B(){cout<<"B constructor"<<endl;}
      ~B(){cout<<"B destructor"<<endl;}
};       

int main()
{
  A * a = new B();
  delete a;    
  getch();
  return 0;   
} 

当我将A的析构函数改为虚函数时,问题得到解决。这是为什么呢?我为什么要将A的析构函数设置为虚函数?我想知道A和B的虚表会发生什么变化。

2个回答

6

虚析构函数只是一个虚函数,因此它遵循相同的规则。

当您调用delete a时,析构函数会隐式调用。如果析构函数不是虚拟的,则会调用a->~A(),因为它像其他非虚拟函数一样被调用。

但是,如果析构函数是虚拟的,则会调用~B(),正如预期的那样:析构函数是虚拟的,因此被调用的是派生类的析构函数,而不是基类的析构函数。

编辑:
请注意,在派生类的析构函数完成后,将隐式调用基类的析构函数。这是与通常的虚函数不同之处。


3
虚函数应该有相同的名称吗? - Bruce
4
@Jack:通常是的;析构函数是一个例外。 - Vlad
2
@Jack:对于析构函数来说是不可能的,它们的名称必须与它们所属的类相同。 - sharptooth
2
幸运的是,编译器将所有析构函数视为相同的函数“destroy()”。因此,虚拟覆盖析构函数是可行的。 - Little Bobby Tables
3
@Jack:C++中没有构造多态性:使用构造函数时,您始终会根据调用的构造函数获得该类的实例。在析构方面,情况更加复杂:您可以通过指向基类的指针销毁对象。 - Vlad
显示剩余14条评论

4
你需要知道的关键是,在上述代码中不使用虚析构函数是未定义行为,这并不是你想要的结果。虚析构函数像其他任何虚函数一样-当你调用`delete`时,程序会在运行时决定调用哪个析构函数,从而解决了你的问题。

@sharptooth:为什么构造函数的情况下不会出现同样的问题? - Bruce
3
当你构造对象时,明确调用了 new B(),因此此时对象明确是 B 类型,因此会调用 B 的构造函数。但是当对象被删除时,你删除的是一个 A* 指针,因此它被视为一个 A 对象,并且除非它是虚拟的,否则将调用 A 的析构函数。 - Tyler McHenry
@Tyler McHenry:非常感谢,这真的为我解决了很多问题 :) - Bruce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接