从析构函数调用虚函数

cpr*_*mer 37 c++ virtual

这样安全吗?

class Derived:  public PublicBase, private PrivateBase
{
 ... 

   ~Derived()
   {
      FunctionCall();
   }

   virtual void FunctionCall()
   {
      PrivateBase::FunctionCall();
   }
}

class PublicBase
{
   virtual ~PublicBase(){};
   virtual void FunctionCall() = 0;
}

class PrivateBase
{
   virtual ~PrivateBase(){};
   virtual void FunctionCall()
   {
    ....
   }
}


PublicBase* ptrBase = new Derived();
delete ptrBase;
Run Code Online (Sandbox Code Playgroud)

此代码有时会在错误的地址中使用IP.

对于每个人来说,在构造函数上调用虚函数并不是一个好主意.

来自像http://www.artima.com/cppsource/nevercall.html这样的文章我明白析构函数也是一个不太适合调用虚函数的地方.

我的问题是"这是真的吗?" 我已经测试过VS2010和VS2005并且调用了PrivateBase :: FunctionCall.未定义的行为?

Dav*_*eas 66

我将反对这里的流程......但首先,我必须假设你的PublicBase析构函数是虚拟的,否则Derived析构函数永远不会被调用.

从构造函数/析构函数调用虚函数通常不是一个好主意

原因是在这两个操作期间动态调度很奇怪.对象的实际类型在构造期间更改,并且在销毁期间再次更改.当一个析构函数被执行时,该对象就是那种类型,而不是从它派生的类型.动态调度始终有效,但虚拟函数的最终覆盖将根据您在层次结构中的位置而更改.

也就是说,您永远不应期望在构造函数/析构函数中对虚函数的调用以从正在执行的构造函数/析构函数的类型派生的任何类型执行.

在您的特定情况下,最终的覆盖(至少在层次结构的这一部分)高于您的级别.而且,您根本不使用动态调度.该呼叫PrivateBase::FunctionCall();是静态解析的,实际上相当于对任何非虚函数的调用.函数是否为虚拟的事实不会影响此调用.

所以是的,你正在做的很好,虽然你会被迫在代码审查中解释这一点,因为大多数人都学习规则的咒语而不是理由.


Mik*_*our 21

这样安全吗?

是.从构造函数或析构函数调用虚函数调度该函数,就好像该对象的动态类型是当前正在构造或销毁的那样.在这种情况下,它是从析构函数调用的Derived,所以它被派遣到Derived::FunctionCall(在你的情况下,PrivateBase::FunctionCall非虚拟调用).所有这一切都很明确.

从构造函数或析构函数调用虚函数是"不是一个好主意"有三个原因:

  • 如果从基类调用它并且(错误地)期望将其分派到派生类中的覆盖,它将导致意外行为;
  • 如果它是纯虚拟的,它将导致未定义的行为;
  • 你将继续向那些认为总是错误的人解释你的决定.