简体   繁体   English

从析构函数调用虚函数

[英]Calling virtual function from destructor

Is this safe ? 这样安全吗?

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;

This code crases sometimes with IP in a bad address. 有时由于IP地址错误,此代码会崩溃。

That is not a good idea to call a virtual function on constructor is clear for everyone. 在每个人都知道在构造函数上调用虚函数不是一个好主意。

From articles like http://www.artima.com/cppsource/nevercall.html I understand that destructor is also a not so good place to call a virtual function. http://www.artima.com/cppsource/nevercall.html之类的文章中,我了解到,析构函数也不是调用虚拟函数的好地方。

My question is "Is this true ?" 我的问题是“这是真的吗?” I have tested with VS2010 and VS2005 and PrivateBase::FunctionCall is called. 我已经用VS2010和VS2005测试过,并且调用了PrivateBase :: FunctionCall。 Is undefined behavior ? 是未定义的行为吗?

I am going to go against the flow here... but first, I must assume that your PublicBase destructor is virtual, as otherwise the Derived destructor will never be called. 我将违背这里的流程...但是首先,我必须假定您的PublicBase析构函数是虚拟的,否则将永远不会调用Derived析构函数。

It is usually not a good idea to call a virtual function from a constructor/destructor 从构造函数/析构函数调用虚拟函数通常不是一个好主意

The reason for this is that dynamic dispatch is strange during these two operations. 原因是在这两个操作期间动态调度很奇怪。 The actual type of the object changes during construction and it changes again during destruction. 对象的实际类型在施工期间发生变化 ,但破坏过程中再次发生变化 When a destructor is being executed, the object is of exactly that type, and never a type derived from it. 当执行析构函数时,对象正是该类型,而不是从其派生的类型。 Dynamic dispatch is in effect at all time, but the final overrider of the virtual function will change depending where in the hierarchy you are. 动态调度始终有效,但是虚拟函数的最终替代程序将根据层次结构中的位置而变化。

That is, you should never expect a call to a virtual function in a constructor/destructor to be executed in any type that derived from the type of the constructor/destructor being executed. 也就是说,您永远不要期望对构造函数/析构函数中的虚函数的调用以从执行的构造函数/析构函数的类型派生的任何类型执行。

But

In your particular case, the final overrider (at least for this part of the hierarchy) is above your level. 在您的特定情况下, 最终的替代项(至少对于层次结构的此部分而言) 您的级别之上 Moreover, you are not using dynamic dispatch at all. 而且,您根本没有使用动态调度 The call PrivateBase::FunctionCall(); 调用PrivateBase::FunctionCall(); is statically resolved, and effectively equivalent to a call to any non-virtual function. 是静态解析的,并且实际上等效于对任何非虚拟函数的调用。 The fact that the function is virtual or not does not affect this call. 函数是否为虚拟函数这一事实不影响此调用。

So yes it is fine doing as you are doing, although you will be forced to explain this in code reviews as most people learn the mantra of the rule rather than the reason for it. 所以是的 ,尽管您将被迫在代码审查中解释这一点,因为大多数人都了解该规则的原理,而不是其原因,所以这样做是很好的。

Is this safe ? 这样安全吗?

Yes. 是。 Calling a virtual function from a constructor or destructor dispatches the function as if the object's dynamic type were that currently being constructed or destroyed. 从构造函数或析构函数调用虚拟函数会分派该函数,就像该对象的动态类型是当前正在构造或销毁的那样。 In this case, it's called from the destructor of Derived , so it's dispatched to Derived::FunctionCall (which, in your case, calls PrivateBase::FunctionCall non-virtually). 在这种情况下,它是从Derived的析构函数调用的,因此它被分派到Derived::FunctionCall (在您的情况下,它是非虚拟地调用PrivateBase::FunctionCall )。 All of this is well defined. 所有这些都是定义明确的。

It's "not a good idea" to call virtual functions from a constructor or destructor for three reasons: 从构造函数或析构函数中调用虚函数是“一个好主意”,原因有以下三个:

  • It will cause unexpected behaviour if you call it from a base class and (erroneously) expect it to be dispatched to an override in a derived class; 如果您从基类中调用它,并且(错误地)期望将其分派到派生类中的重写,则它将导致意外的行为。
  • It will cause undefined behaviour if it is pure virtual; 如果它是纯虚拟的,它将导致不确定的行为;
  • You'll keep having to explain your decision to people who believe that it's always wrong to that. 您将不得不继续向那些认为这样做总是错误的人解释您的决定。

In general, it is not a good idea to call a virtual function, unless the object of the class it might get dispatched to (ie, the "full" object of the most-derived class) is fully-constructed. 通常,调用虚拟函数不是一个好主意,除非它可能被分派到该类的对象(即,派生最多的类的“完整”对象)是完全构造的。 And this is not the case 事实并非如此

  • until all the constructors finish execution 直到所有构造函数完成执行
  • after any destructor finishes execution 在任何析构函数完成执行之后

It is a very bad idea according to scott: link 斯科特说,这是一个非常糟糕的主意: 链接

This is what i have compiled and run to help myself gain a better understanding of the destruction process, you might also find it helpful 这是我为了帮助自己更好地了解销毁过程而编写并运行的,您可能也会发现有帮助

#include <iostream>
using namespace std;


class A {
public:
  virtual void method() {
    cout << "A::method" << endl;
  }

  void otherMethod() {
    method();
  }

  virtual ~A() {
    cout << "A::destructor" << endl;
    otherMethod();
  }

};

class B : public A {
public:
  virtual void method() {
    cout << "B::method" << endl;
  }

  virtual ~B() {
    cout << "B::destructor" << endl;
  }
};

int main() {

  A* a = new B();

  a->method();

  delete a;

}

Is this safe ? 这样安全吗?

Yes and No. 是和否

Yes, because your example as-is is well defined and will work. 是的,因为您的示例按原样定义良好并且可以正常工作。 All of that is well explained by other answers. 所有这些都可以通过其他答案很好地解释。 Also, this code is totally safe because it won't compiler the way it's written: private dtors in base classes etc. 另外,这段代码是完全安全的,因为它不会以编写的方式进行编译:基类中的私有dtor等。

The reason it's not safe to do the way your example does is because this code assumes that nobody else will override FunctionCall from your Derived class and won't expect that override to be called on object destruction. 用示例的方式进行操作并不安全的原因是,此代码假定没有其他人会覆盖Derived类中的FunctionCall并且不希望在销毁对象时调用该重写。 Most likely compilers will complain about this. 最有可能的编译器会对此抱怨。 You can improve your code by marking your FunctionCall as final : 您可以通过将FunctionCall标记为final来改进代码:

class Derived : public PublicBase, private PrivateBase
{
 ... 
   virtual void FunctionCall() final;
}

or your Derived class as final : 或您的Derived类为final

class Derived final : public PublicBase, private PrivateBase
{
 ... 
   virtual void FunctionCall();
}

If you compile on older compilers or cannot use c++11 for any other reason, then you may at least be more explicit here and write in your code exactly what will happen at runtime regardless if FunctionCall is overridden by any descendant of your Derived class: 如果您在较旧的编译器上进行编译,或者由于任何其他原因而无法使用c ++ 11 ,那么您至少可以在此处更加明确,并在代码中准确地写出运行时将发生的情况,而无论FunctionCall是否被Derived类的任何后代覆盖:

class Derived : public PublicBase, private PrivateBase
{
 ... 
   ~Derived()
   {
      Derived::FunctionCall(); // be explicit which FunctionCall will be called
   }

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM