为什么在析构函数中将虚表设置回该级别

Why in destructors is the virtual table set back to that level?

本文关键字:设置 虚表 析构函数 为什么      更新时间:2023-10-16

在大多数派生类的析构函数中调用纯虚函数之后,我尝试了一些代码来检查一些语法,发现在调用连续的析构函数时,它们调用了相关的虚函数。考虑以下代码:

class Base
{
public:
virtual void Method() = 0;
};
class Derived :  public Base
{
public:
~Derived()
{
    Method();
}
virtual void Method()
{
    cout << "D";
}
};
class DoubleD : public Derived
{
public:
~DoubleD()
{
    Method();
}
virtual void Method()
{
    cout << "DD";
}
};
int main(array<System::String ^> ^args)
{
    DoubleD D;
    DoubleD E;
    return 0;
}

如预期的那样,当对象被析构时,它调用正确的方法(例如首先是最派生的方法,然后是第二个最派生的方法)。

输出:DD D

我的问题是,为什么这个工作?既然您不打算在c'tor/d'tor中调用虚拟函数,那么为什么虚拟表会正确地"展开"呢?

例如,我可以看到为什么最派生的一个工作,这是虚函数指针表在这个开始时的状态。但是,当调用Derived的析构函数时,为什么表被正确地设置为指向Method的类实现呢?

为什么不保留它,或者如果它是好的,将值设置为NULL。

由于您不打算在c'tor/d'tor中调用虚函数,为什么虚拟表可以正确地"unwind"?

前提是错误的。从构造函数或析构函数调用虚函数并没有错,只要您知道它们是如何工作的。正如您所看到的,动态类型是正在运行的构造函数或析构函数的类型,因此不会对尚未构造或已经销毁的对象部分进行虚调用。

行为定义得很好。你不应该担心你的编译器供应商是如何实现它的(尽管这并不难自己推理出来,或者只是查找)。

通常不建议在析构函数中调用虚函数,因为它的行为不直观,但它没有根本的问题。

根据标准,它应该是这样工作的。

至于为什么,在为派生类运行析构函数之后,不能指望该类的任何属性是有效或一致的。此时调用虚方法中的一个,如果它进入派生类方法,将是一场灾难。

编译器很可能完全绕过虚函数表,因为它已经知道哪个被覆盖的方法应用于对象的当前状态。这只是一个实现细节

在对象创建时初始设置后,虚拟表不会在运行时被修改。在一些实现中,虚拟表应该按照类的基础来创建。

在你的例子中,当double对象被销毁时,它调用double类中的方法函数,因为对象的double部分还没有完全销毁。double类的VTable有一个方法函数的入口,当它被重写(在继承的最后一层)时,它指向类中的方法

一旦double被销毁,现在对象类型为派生类型。所以调用必须去类派生的vtable中的方法。