【发布时间】:2014-09-30 04:02:28
【问题描述】:
我对以下问题感到困惑(MSVC++ 2012):
我有一组定义接口的纯虚拟类和一些用于实现的具有多重继承的派生类。我使用同一组基类来实现两个不同的端类(在随后的类层次结构中,“Foo”类为这两个端类中的每一个实现不同的虚函数)。这两个最终类之一编译没有问题,但另一个将其 vfptr 表条目混合在一起。我对可能导致此问题的原因感到困惑,并且无法指出错误。所以我把它提交给这里的 C++ 专家。
这是简化的类层次结构:
class IBaz1 {
virtual void IBaz1_SC() = 0;
virtual void IBaz1_IVR() = 0;
virtual void IBaz1_ICIVR() = 0;
};
class IBaz2 {
virtual void IBaz2_JTHL() = 0;
virtual void IBaz2_UP() = 0;
virtual void IBaz2_RKF() = 0;
virtual void IBaz2_GVC() = 0;
virtual void IBaz2_GAI() = 0;
};
class IBaz3 {
virtual void IBaz3_SCJ() = 0;
virtual void IBaz3_CJS() = 0;
};
class IQux {
virtual void IQux_RIU() = 0;
virtual void IQux_CTAU() = 0;
virtual void IQux_D() = 0;
virtual void IQux_AD() = 0;
virtual void IQux_IDIP() = 0;
virtual void IQux_GLT() = 0;
virtual void IQux_SLT() = 0;
virtual void IQux_GF() = 0;
};
class Qux : public IQux {
virtual void Qux::IQux_D();
virtual void Qux::IQux_AD();
virtual void Qux::IQux_IDIP();
};
class Bar : public IBaz1, public IBaz2, public IBaz3, public Qux {
virtual void IBaz1_ICIVR();
virtual void IBaz2_UP();
virtual void IQux_RIU();
virtual void IQux_GLT();
virtual void IQux_SLT();
virtual void IQux_GF();
};
class FooA (and FooB) : public Bar {
virtual void IBaz1_SC();
virtual void IBaz1_IVR();
virtual void IBaz2_JTHL();
virtual void IBaz2_RKF();
virtual void IBaz2_GVC();
virtual void IBaz2_GAI();
virtual void IBaz3_SCJ();
virtual void IBaz3_CJS();
virtual void IQux_CTAU();
};
Expected vfptr result mixed-up vfptr
====================== ====================================
FooA (is also a COM) FooB (is a straight inheritance)
Bar Bar
IBaz1 IBaz1
[0] Foo::IBaz1_SC() [0] Foo::IBaz1_SC()
[1] Foo::IBaz1_IVR() [1] Foo::IBaz1_IVR()
[2] Bar::IBaz1_ICIVR() [2] Bar::IBaz1_ICIVR()
IBaz2 IBaz2
[0] Foo::IBaz2_JTHL() [0] Bar::IQux_GLT()
[1] Bar::IBaz2_UP() [1] Bar::IQux_SLT()
[2] Foo::IBaz2_RKF() [2] Foo::IBaz2_JTHL()
[3] Foo::IBaz2_GVC() [3] Bar::IBaz2_UP()
[4] Foo::IBaz2_GAI() [4] Foo::IBaz2_RKF()
IBaz3 IBaz3
[0] Foo::IBaz3_SCJ() [0] Foo::IBaz3_SCJ()
[1] Foo::IBaz3_CJS() [1] Foo::IBaz3_CJS()
Qux Qux
IQux IQux
[0] Bar::IQux_RIU() [0] Bar::IQux_RIU()
[1] Foo::IQux_CTAU() [1] Foo::IQux_CTAU()
[2] Qux::IQux_D() [2] Qux::IQux_D()
[3] Qux::IQux_AD() [3] Qux::IQux_AD()
[4] Qux::IQux_IDIP() [4] Qux::IQux_IDIP()
[5] Bar::IQux_GLT() [5] [thunk]:Bar::IQux_GLT`adjustor{8}'()
[6] Bar::IQux_SLT() [6] [thunk]:Bar::IQux_SLT`adjustor{8}'()
[7] Bar::IQux_GF() [7] [thunk]:Bar::IQux_GF`adjustor{8}'()
两个端类是“FooA”和“FooB”。在实际代码中,它们的定义方式相同,只是 FooA 也是一个 AxtiveX COM 对象,因此除了继承自 Bar,因此继承自 IBaz1、IBaz2、IBaz3 和 Qux 之外,它还继承自许多 ATL 模板。
我已将名称缩减为大写,以便它们适合页面宽度。比如IBaz1_SC的真名在实际代码中是“SetCapture”,IBaz1_IVR真名是“InvalidateViewportRect”。
我并排显示了两个 vfptr 表,以便于比较它们。我展示的 vfptr 表布局是我在为所有继承类展开 vtable 后从 MSVC 调试器“自动”窗口获得的布局。左边的 vfptr 表来自构建和运行良好的最终类,即“FooA”,它也是一个 COM 继承类。而正确的 vfptr 表来自未运行的最终类,即“FooB”,它不是 COM 继承的类,而只是一个直接继承的类。
注意 IBaz2 vfptr 中的条目。 IBaz2[0] 中的指针已下推至 IBaz2[2],IBaz2[1] 下推至 IBaz2[3] 等。IBaz2[0] 和 [1] 中的条目指向来自 IQuz[5] 的函数和 [6]。现在 IQux[5] 到 [7] 中的指针正在经历一个 thunk 并且有一个调整器。
程序在某些时候崩溃,它调用 IBaz2_JTHL 而不是调用 IBaz2_RKF。
这可能是什么原因造成的?我应该寻找什么样的编码错误?任何可能有助于理解问题和解决问题的解释将不胜感激。
如果需要,我可以提供更好的代码摘录。
【问题讨论】:
-
某事告诉我您的问题在于您尚未发布的最终课程代码,如果它适用于其中一个而不适用于另一个,您为什么不尝试发布不起作用的课程?在不相关的说明中,您的所有类都没有
virtual析构函数,除非您只使用堆栈上的对象,否则这很危险。 -
对不起,对我来说代码几乎无法阅读。如果真的有问题,是否可以将示例最小化到读者能够发现问题的程度?源代码中不可读的名称是破坏代码的第一步。
-
“其 vfptr 表条目混淆了”。那是你对事实的解释。让我们先看看事实本身。你不是在展示它们。如果您的程序有意外行为,请描述预期行为和实际行为,并显示完整的源代码以便重现。
-
为什么没有虚拟析构函数?
-
我已经编辑了 OP,试图让两个 enc 类更加明确,解释示例中使用的名称(这不仅仅是一个玩具示例,这是来自一个拥有数十万的真实应用程序代码行,仅涉及的实际类将长达几页),并使其更明确地预期结果与获得的结果。