我对以下问题感到困惑(MSVC++ 2012):
我有一组定义接口(interface)的纯虚拟类,以及一些具有多重继承的派生类以实现。我使用相同的基类集来实现两个不同的结束类(在随后的类层次结构中,“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]中的指针正在经历重击并具有调整器。
该程序在某个时刻崩溃,它调用IBaz2_JTHL而不是调用IBaz2_RKF。
是什么原因造成的?我应该寻找哪种编码错误?我们将不胜感激任何可能有助于理解该问题并解决该问题的解释。
如果需要,我可以提供更好的代码摘录。
最佳答案
从提出的问题来看,很明显,最终的vtable布局与您的期望不符。这很可能是由于您的期望,而不是其他任何原因。
调节器重击通常是由非零偏移量的基类引起的,而偏移量又由多重继承引起。
已知MSVC折叠相同的功能,这可能会引起混淆。例如,如果其中一些功能是无操作,则它们的vtable条目可能都指向相同的无操作实现。
您的代码无法运行,可能是因为您绕过了C++并直接访问了vtable(我之所以这样认为是因为您显示了vtable布局,甚至在C++标准中也没有定义)
[编辑]
新信息几乎证实了这一点:非标准访问= COM。 COM不如C++强大,并且不能处理多重继承。 COM错误地解释了使用MI的C++类中的vtable,从而导致了上述问题。