3

我正在使用 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 实现?

4

3 回答 3

2

这里的问题是破坏顺序:

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

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

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

于 2013-01-24T14:27:22.263 回答
1

这是正确的。没有办法Helloa. 这同样适用于构造函数。

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

于 2013-01-24T14:24:46.057 回答
1

根据 12.7/4,

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

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

于 2013-01-24T14:29:56.857 回答