【问题标题】:vfptr / vftable changes basetype when class destructor calls parent destructor当类析构函数调用父析构函数时,vfptr / vftable 更改基类型
【发布时间】:2013-01-24 14:19:02
【问题描述】:

我使用的是 Visual Studio 2008、c++、win32,没有点网。

我遇到了在父级的析构函数中调用的虚函数的问题。 例如,学习 a 和 b 类。 a 类有一个虚函数,叫做 Hello();调用时,它会打印 hello。

类b继承自类a,也实现了函数Hello。调用时,它会打印 world。

在类 a 的析构函数中调用函数 Hello。当类 b 被删除时,首先调用 b 的析构函数,然后调用 a 的析构函数。在 a 函数的析构函数中调用 Hello,除了它是与 a 类关联的实现,而不是 b 类。

此行下方此示例的代码。

class a{
public:
  virtual void Hello();
  a();
  ~a();
};

a::a(){
};

a::~a(){
  Hello();
}

void a::Hello(){
  printf("hello\n");
}

class b:public a{
public:
  virtual void Hello();
  b();
  ~b();  
};

b::b(){
}

b::~b(){
}

void b::Hello(){
  printf("world\n");
}

int _tmain(int argc, _TCHAR* argv[]){  
  a* exampleA=new a();
  b* exampleB=new b(); 
  exampleA->Hello();
  exampleB->Hello();
  delete exampleA;
  delete exampleB;
  return 0;
}

输出是:

hello
world
hello
hello

当我在 b 类的析构函数中放置断点时,局部变量中的 __vfptr 如下:

__vfptr
  [0x0]    0x002314ce b::Hello(void)

当单步进入a类的析构函数时,局部变量中的__vfptr变为:

__vfptr
  [0x0]   0x0024c864 a::Hello(void)

我问这正常吗?并且没有(正确的)方法可以在 a 类的析构函数中调用 b 的 Hello 实现?

【问题讨论】:

    标签: c++ windows visual-studio


    【解决方案1】:

    这里的问题是破坏顺序:

    • 首先,派生类b被销毁
    • 然后是基类a~a() 从已销毁的 b 调用方法(虚拟方法)是不安全的,因为此类方法可以引用/利用 b 中已被销毁的类成员

    我还推荐 Scott Meyers 的这篇文章: http://www.artima.com/cppsource/nevercall.html

    另外,我订阅了 Mark B 的解决方案。此外,Kerrek SB 的引用证实了这是标准的、明确定义的行为。

    【讨论】:

    • +1 因为这是解释为什么而不是说明 OP 似乎已经知道的唯一答案。
    【解决方案2】:

    没错。在a 的析构函数中没有办法b 的Hello。这同样适用于构造函数。

    如果您确实需要在销毁时调用多态的东西,您可以使用私有/受保护的析构函数和首先调用虚函数然后自毁的公共销毁方法来强制执行两次销毁。

    【讨论】:

      【解决方案3】:

      根据 12.7/4,

      当从构造函数或析构函数直接或间接调用虚函数时,[...] 调用的函数是构造函数或析构函数类中的最终覆盖,而不是在更多-派生类

      因此行为是明确定义的,您在类本身中调用成员函数,并且没有动态调度。

      【讨论】:

      • 什么是 12.7/4,我没有得到参考。
      • @user2007560:这是 C++ 标准的相关部分。
      猜你喜欢
      • 1970-01-01
      • 2012-09-02
      • 1970-01-01
      • 2011-04-28
      • 1970-01-01
      • 2014-05-17
      • 2014-10-02
      • 2015-12-22
      • 2014-10-23
      相关资源
      最近更新 更多