当类析构函数调用父析构函数时,vfptr / vftable会更改basetype

时间:2013-01-24 14:19:02

标签: c++ windows visual-studio

我正在使用visual studio 2008,c ++,win32,no dot net。

我遇到了在父类的析构函数中调用的虚函数的问题。 例如,取类a和b。类a有一个名为Hello()的虚函数;当被调用时,它打印你好。

类b继承自类a,并且还实现了函数Hello。当被叫时,它打印世界。

在类a的析构函数中调用函数Hello。当删除b类时,首先调用b的析构函数,然后调用b的析构函数。在函数的析构函数中调用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实现?

3 个答案:

答案 0 :(得分:2)

这里的问题是破坏的顺序:

  • 首先,派生类b被销毁
  • 然后是基类a~a()从被破坏的b调用方法(虚拟方法)是不安全的,因为这样的方法可以引用/使用已经被销毁的b内的类成员

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

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

答案 1 :(得分:1)

这是对的。在Hello的析构函数中无法使用a。这同样适用于构造函数。

如果你真的需要在破坏时使用一个名为polmorphically的东西,你可以使用私有/受保护的析构函数和首先调用虚函数然后自毁的公共破坏方法强制执行两阶段破坏。

答案 2 :(得分:1)

根据12.7 / 4,

  

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

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