我对以下问题感到困惑(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表格布局是我从MSVC调试器获得的布局" Auto"我扩展了所有继承类的vtable后的窗口。左边的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。
可能导致这种情况的原因是什么?我应该寻找什么样的编码错误?任何可能有助于理解问题并解决此问题的解释都将非常感激。
如果需要,我可以提供更好的代码提取。
答案 0 :(得分:1)
从提出的问题来看,很明显,由此产生的vtable布局并不符合您的期望。这很可能是由于你的期望而不是其他任何事情。
调整器thunk通常是由非零偏移的基类引起的,而这些基类又是由多重继承引起的。
已知MSVC折叠相同的功能,这可能会导致混淆。例如,如果其中一些函数是无操作,则它们的vtable条目可能都指向相同的无操作实现。你的代码没有运行可能是因为你绕过C ++并直接访问vtable(我假设这是因为你展示了一个vtable布局,它甚至没有在C ++标准中定义)
<强> [编辑] 强>
新信息几乎证实了这一点:非标准访问= COM。 COM没有C ++强大,也无法处理多重继承。 COM误解了使用MI的C ++类中的vtable,导致上面出现的问题。
答案 1 :(得分:0)
所以我找到了这个vtable混音的原因。 IBaz2类定义了两次。第二个定义是在一个旧的源文件中,该文件刚刚从项目中删除,但仍然在磁盘上并在预编译的头文件中引用。
这个第二个和旧的定义具有在错误的vtable中显示的所有纯虚函数。我假设,因为它在预编译头中,旧的定义在编译时用于vtable布局,但访问它的代码是从新定义生成的。
为了弄清楚这一点,我开始重命名所涉及的类。在修复与重命名相关的问题时,我偶然发现了对旧文件的引用。
现在一切都按预期工作了。我本以为编译器会抱怨类的第二个定义。