关注这个问题 - 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。
答案 0 :(得分:5)
因为你不打算在c'tor / d'tor中调用虚函数, 为什么虚拟表会正确“展开”。
前提是错误的。从构造函数或析构函数调用虚函数没有任何问题,前提是您知道它们是如何工作的。如您所见,动态类型是正在运行的构造函数或析构函数的类型,因此您不会对尚未构造或已被销毁的对象部分进行虚拟调用。
答案 1 :(得分:2)
行为完全明确。您不应该担心编译器供应商如何设法实现它(尽管自己推理起来并不是很难,或者只是查找)。
由于非直观的行为,通常不会建议在析构函数中调用虚函数,但它没有任何根本性的错误。
答案 2 :(得分:1)
这是按照标准运作的方式。
至于 why ,在为派生类运行析构函数之后,您不能指望该类的任何属性有效或一致。如果它进入派生类方法,那么在那一点调用其中一个虚拟方法将是一场灾难。
编译器很可能完全绕过vtable,因为它已经知道哪个重写方法适用于对象的当前状态。这只是一个实现细节。
答案 3 :(得分:0)
在对象创建初始设置后,虚拟表在运行时不会被修改。 在某些实现中,虚拟表应按类创建。
在您的示例中,当DoubleD对象被销毁时,它会调用DoubleD类中的方法函数,因为,对象的DoubleD部分尚未完全销毁。 DoubleD类的VTable有一个方法函数的条目,当它被重写时(在最后一级继承中)指向其类中的方法
一旦销毁了DoubleD,现在对象类型的类型为Derived。所以调用必须转到类Derived的vtable中的方法。因此行为。