破坏对象会停止后期绑定吗?

Does destructing objects stop the late binding?

考虑以下 C++ classes 继承层次结构及其预期的多态行为。

#include <iostream>
using namespace std;

class A
{
public:
  A () { cout << "A constructor\n"; }
  virtual void display() { cout << "A display\n"; }
  virtual ~A() { cout << "A destructor\n"; }
  friend ostream& operator << (ostream &out, A &a) {
    a.display();
    return out;
  }
};

class B : public A
{
public:
  B () { cout << "B constructor\n"; }
  virtual void display() { cout << "B display\n"; }
  virtual ~B() { cout << "B destructor\n"; }
};

class C : public B
{
public:
  C () { cout << "C constructor\n"; }
  virtual void display() {cout << "C display\n";}
  virtual ~C() { cout << "C destructor\n"; }
};
int main()
{
  C c1;
  cout << endl; c1.display(); cout << endl;
  c1.~C();
  cout << endl; c1.display(); cout << endl;
  c1.~C();

  cout << "=================================================" << endl;

  C c2;
  cout << endl << c2 << endl;
  c2.~C();
  cout << endl << c2 << endl;
  c2.~C();

  return 0;
}

我的理解是 display 成员函数是虚拟的,因此将始终如此。在主程序的顶部,它表现良好;那就是当我们调用 c1.display() 时它会打印“C 显示”消息。即使在销毁 c1 对象之后也是如此。

在下半部分,我们不调用显示函数。相反,我们调用输出流友元函数,该函数将依次调用显示成员函数。在这种情况下,它一开始工作得很好;即 cout << endl << c2 << endl;打印 C 显示。这是意料之中的,因为我们通过引用将 c1 对象传递给 ostream 友元函数,因此后期绑定将负责执行哪个显示成员函数。

但是当我们做同样的事情时 cout << endl << c2 << endl;析构 c2 对象后,奇怪的是 display 成员函数不再按预期运行。它调用基本 class 显示并打印“A 显示”消息。

我没看懂?销毁对象会停止后期绑定吗?

销毁对象会使继续调用该对象的成员函数变得非法。您的代码的行为是未定义的,并且您观察到的任何行为都不能依赖于在同一程序的 运行 中持续存在,更不用说不同的编译器标志或完全不同的实现了。

但是,我们可以猜测您为什么会看到您所报告的特定行为,前提是我们不应该继续编译并且 运行 将来会出现此类代码,因为它的行为是无法保证的。

很可能你的编译器正在去虚拟化 c1.display() 调用,因为它可以看到 c1 的定义(因此知道它的动态类型是 C)。因此,生成的代码可能根本不查询 vtable。这解释了为什么您继续看到“C 显示”,即使对象已经被销毁。

c2 的情况下,对象通过引用传递,编译器可能没有足够积极地内联以去虚拟化最终的 display 调用。如果它在 c2 被销毁后查询 vtable,它可能会找到 A 的 vtable,因为 C 对象的销毁过程最终必须将 A 子对象的 vptr 重置为在执行 A::~A 之前指向 A vtable(以防 A::~A 调用任何虚函数)。这样就解释了“A 显示”消息。