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

时间:2013-05-13 20:42:57

标签: c++ virtual-functions

关注这个问题 - Pure virtual call in destructor of most derived class - 我尝试了一些代码来检查一些语法,并发现当调用过多的析构函数时,他们会调用相关的虚函数。请考虑以下代码:

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。

4 个答案:

答案 0 :(得分:5)

  

因为你不打算在c'tor / d'tor中调用虚函数,   为什么虚拟表会正确“展开”。

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

答案 1 :(得分:2)

行为完全明确。您不应该担心编译器供应商如何设法实现它(尽管自己推理起来并不是很难,或者只是查找)。

由于非直观的行为,通常不会建议在析构函数中调用虚函数,但它没有任何根本性的错误。

答案 2 :(得分:1)

这是按照标准运作的方式。

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

编译器很可能完全绕过vtable,因为它已经知道哪个重写方法适用于对象的当前状态。这只是一个实现细节。

答案 3 :(得分:0)

在对象创建初始设置后,虚拟表在运行时不会被修改。 在某些实现中,虚拟表应按类创建。

在您的示例中,当DoubleD对象被销毁时,它会调用DoubleD类中的方法函数,因为,对象的DoubleD部分尚未完全销毁。 DoubleD类的VTable有一个方法函数的条目,当它被重写时(在最后一级继承中)指向其类中的方法

一旦销毁了DoubleD,现在对象类型的类型为Derived。所以调用必须转到类Derived的vtable中的方法。因此行为。