2

Following this question - Pure virtual call in destructor of most derived class - I tried some code to check some syntax and discovered that as sucessive destructors are called, they call their relevant virtual functions. Consider this code:

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;
}

As expected, as the object gets destructed, it calls the correct method (eg first the most derived and then the the second most derived).

Ouput: DD D

My question is, why does this work? Since you are not meant to call virtual functions in a c'tor/d'tor, why does the virtual table "unwind" correctly.

Eg, I can see why the most derived one works, that was the state the virtual function pointer table was in when this started. But why, when Derived's destructor is called, does the table get correctly set to point at that classes implementation of Method.

Why not just leave it, or if it is being nice, set the value to NULL.

4

4 回答 4

6

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

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

于 2013-05-13T20:59:39.193 回答
3

行为是完美定义的。你不应该担心你的编译器供应商是如何设法实现它的(尽管你自己推理并不难,或者只是查找)。

由于不直观的行为,通常不建议在析构函数中调用虚函数,但从根本上来说并没有错。

于 2013-05-13T20:46:22.847 回答
2

这就是它应该按照标准工作的方式。

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

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

于 2013-05-13T21:23:17.053 回答
1

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

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

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

于 2013-05-13T21:03:12.333 回答