当多个继承类本身在其层次结构中具有菱形继承时,函数的模糊继承

时间:2015-02-17 07:35:54

标签: c++ inheritance multiple-inheritance virtual-inheritance msvc12

Word说明以下代码):我有一个提供类集合的库。对于每组课程,我们有两种具体类型,(ClassA_PartialClassA),(ClassB_PartialClassB)等。每个实现(Interface_PartialInterface)。此外,Interface Interface_Partial,每个Class? Class?_Partial - 创建钻石继承模式,其中top是虚拟遗传的。

为什么Interface_Partial在继承ClassAClassB时功能不明确?

struct Interface_Partial
{ 
    virtual ~Interface_Partial(); 
    virtual void f() = 0;
};

struct Interface
    :
    virtual Interface_Partial
{
    virtual void g() = 0;
};


struct ClassA_Partial : public virtual Interface_Partial
{
    void f() {};
};


struct ClassA : public Interface, public virtual ClassA_Partial
{
    void g() {};
};

struct ClassB_Partial : public virtual Interface_Partial
{
    void f() {};
};


struct ClassB : public Interface, public virtual ClassB_Partial
{
    void g() {};
};

struct MyClass : public ClassA, public ClassB
{ }; // error C2250: MyClass : ambiguous inheritance of 'void Interface_Partial::f(void)'

为什么我们不能在我们多次继承公共接口时消除我们通常做的方式的歧义?例如

struct ClassX : public Interface_Partial { void f() {} };
struct ClassY : public Interface_Partial { void f() {} };
class Another : public ClassX, public ClassY
{};

void func()
{
    // This is ok
    Another a;
    a.ClassX::f();   

    // Why would this not work?
    // unambiguously refers to the one and only f() function 
    // inherited via  ClassA
    MyClass b;
    b.ClassA::f();   
}

2 个答案:

答案 0 :(得分:4)

要回答您的两个问题,我们需要了解虚函数的工作原理 -

如果你为每个类绘制vtable,你可以理解为什么它会抛出模糊的继承错误

enter image description here

对于 Interface_Partial :它的Vtable将包含函数f()的地址及其自己的定义,即 Interface_Partial :: f()。

接口类/结构中:它具有自己的定义的函数g()以及它没有覆盖的Interface_Partial的继承函数f()。因此接口类的Vtable将具有两个函数的地址:

  1>    g() as  Interface :: g()

  2>    f() as  Interface_Partial :: f()

现在来到 ClassA_Partial :它已从其父类覆盖函数f()并给出了自己的定义,因此 ClassA_Partial 的vtable将具有函数f() as:

ClassA_Partial :: f()

现在主要部分:

如果您看到 ClassA ,它继承自接口 ClassA_Partial 并覆盖g(),但不是f() 即可。因此,当编译器看到这个时,它会被混淆,因为现在它将具有两个f()的定义

 1> as   Interface_Partial :: f()   
 2> as   ClassA_Partial :: f()

选择哪一个? Interface_Partial或ClassA_Partial!所以即使你这样做也会引发错误

 b.ClassA::f();

由于f()

的两个不同版本

答案 1 :(得分:2)

由于虚拟继承,基类Interface_Partial只有一个vtable - 一旦使用虚拟继承,“虚拟”会感染所有级别的所有派生类

继承是不明确的,因为MyClass有两个不同版本的f()可用 - 一个来自ClassA,另一个来自ClassB。由于Interface_Partial的虚拟继承,您有两个派生类实现处于同一级别并尝试覆盖相同的虚函数。声明虚拟基类使所有派生类共享虚拟基类,包括其vtable。共享vtable更新为包含应该调用的虚函数的指针。但由于有两个同样“好”的可供选择,所以没有办法选择其中一个。

在您给出的另一个示例中,Interface_PartialClassXClassY的非虚基类,因此每个类都会覆盖完全不同的虚函数。这对编译器来说是毫不含糊的,但是当您调用它们时,您必须指定要调用的特定f()

您可以通过在f()中提供MyClass的实施来解决此问题。