我对以下问题感到困惑(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,从而导致了上述问题。

10-08 11:51