C++ 从析构函数调用虚函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12092933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:52:26  来源:igfitidea点击:

Calling virtual function from destructor

c++virtual

提问by cprogrammer

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 sometimeswith 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.htmlI 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. Is undefined behavior ?

我的问题是“这是真的吗?” 我已经用 VS2010 和 VS2005 进行了测试,并调用了 PrivateBase::FunctionCall。是未定义的行为吗?

回答by David Rodríguez - dribeas

I am going to go against the flow here... but first, I must assume that your PublicBasedestructor is virtual, as otherwise the Deriveddestructor 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 changesduring construction and it changesagain 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 overriderof 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 finaloverrider (at least for this part of the hierarchy) is aboveyour level. Moreover, you are not using dynamic dispatchat all. The call PrivateBase::FunctionCall();is statically resolved, and effectively equivalent to a call to any non-virtual function. The fact that the function is virtualor not does not affect this call.

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

So yesit 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.

所以是的,按照你的方式做是很好的,尽管你将被迫在代码中解释这一点,因为大多数人学习规则的口头禅而不是它的原因。

回答by Mike Seymour

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::FunctionCallnon-virtually). All of this is well defined.

是的。从构造函数或析构函数调用虚函数会分派函数,就好像对象的动态类型是当前正在构造或销毁的类型。在这种情况下,它是从 的析构函数调用的Derived,因此它被分派到Derived::FunctionCall(在您的情况下,它PrivateBase::FunctionCall是非虚拟调用的)。所有这些都有明确的定义。

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.
  • 如果您从基类调用它并且(错误地)期望它被分派到派生类中的覆盖,它将导致意外行为;
  • 如果它是纯虚拟的,它将导致未定义的行为;
  • 你将不得不不断向那些认为它总是错误的人解释你的决定。

回答by Grzegorz Herman

In general, it is not a good idea to call a virtual function, unless the object of the class it might get dispatched to (i.e., 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
  • 直到所有的构造函数都执行完毕
  • 在任何析构函数完成执行后

回答by sji

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;

}

回答by Pavel P

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.

是的,因为您的示例定义明确并且可以工作。所有这些都在其他答案中得到了很好的解释。此外,这段代码是完全安全的,因为它不会按照它的编写方式编译:基类中的私有 dtors 等。

The reason it's not safe to do the way your example does is because this code assumes that nobody else will override FunctionCallfrom your Derivedclass andwon't expect that override to be called on object destruction. Most likely compilers will complain about this. You can improve your code by marking your FunctionCallas final:

以您的示例的方式执行操作不安全的原因是,此代码假定没有其他人会FunctionCall从您的Derived类中覆盖,并且不会期望在对象销毁时调用该覆盖。很可能编译器会抱怨这个。您可以通过将您的代码标记FunctionCallfinal来改进您的代码:

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

or your Derivedclass as final:

或者你的Derived班级作为决赛

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

If you compile on older compilers or cannot use c++11for 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 FunctionCallis overridden by any descendant of your Derivedclass:

如果您在较旧的编译器上编译或由于任何其他原因不能使用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();
   }
}