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

Posted

技术标签:

【中文标题】当在其层次结构中具有菱形继承的类的多重继承时,函数的模糊继承【英文标题】:Ambiguous inheritance of function when multiple inheritance of classes that themselves have diamond inheritance in their hierarchy 【发布时间】:2015-02-17 07:35:53 【问题描述】:

单词描述下面的代码):我有一个提供类集合的库。对于每组类,我们有两个具体的类型,(ClassA_Partial, ClassA), (ClassB_Partial, ClassB) 等等。每个分别实现(Interface_Partial, Interface)。此外,Interface 是一个 Interface_Partial 和每个Class? 是一个 Class?_Partial - 创建一个菱形继承模式,其中顶部是虚拟继承的。

当同时继承ClassAClassB 时,为什么Interface_Partial 函数不明确?

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();   

【问题讨论】:

幸运的是,上面示例中的“库”是我自己的代码——为了保持理智,我已经摆脱了钻石继承。但是 - 我对上述方法不起作用的技术原因感兴趣。 【参考方案1】:

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

如果您为每个类绘制 vtable,那么您就可以理解为什么它会引发模棱两可的继承错误

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

Interface 类/结构中:它具有具有自己定义的函数 g() 以及从未覆盖的 Interface_Partial 继承的函数 f()。所以 Interface 类的 Vtable 将有两个函数的地址:

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

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

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

ClassA_Partial :: f()

现在是主要部分:

如果您看到 ClassA 它继承自 InterfaceClassA_Partial 并覆盖 g() 但 不是 f() 。所以当编译器看到这个时,它会感到困惑,因为现在它会有 f() 的两个定义

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

选择哪一个? Interface_Partial 或 ClassA_Partial !所以即使你这样做,它也会抛出一个错误

 b.ClassA::f();

因为 f() 的两个不同版本

【讨论】:

我还是不太明白你的解释。如果我实例化ClassA,那么ClassA::f() 由于虚拟继承是明确的,所以我仍然认为b.ClassA::f() 是明确的。 因为 ClassA 有两个函数 f() 的定义,一个来自 Interface_Partial,另一个来自 ClassA_Partial。由于您使用virutal继承Interface和ClassA_Partial,因此它们将拥有函数f()的一份副本,但是classA_Partial覆盖了f()并创建了自己的f()版本,现在当您继承这两个Interface和ClassA_Partial时,在类中A 你会得到两个不同版本的 f()。【参考方案2】:

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

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

在您给出的另一个示例中,Interface_PartialClassXClassY 的非虚拟基类,因此每个类都覆盖了完全不同的虚拟函数。这对编译器来说是明确的,尽管当你调用它们时,你必须指定你想调用哪个特定的f()

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

【讨论】:

我不明白这与Another 类示例有何不同,尽管在同一级别有两个派生类实现,但我们毫无歧义地继承了ClassXClassY。你能澄清一下吗? 那么是不是虚拟继承会向下传递给子类呢?即使我只想知道ClassAClassB - 我仍然需要知道它们中的每一个实际上都继承了f(),这意味着如果我非虚拟 继承ClassA 并且ClassB 编译器仍在尝试虚拟继承 f() 并解析单个函数,即使我实际上想要 ClassA::f()ClassB::f() @Zero,因为虚拟继承,基类Interface_Partial只有一个vtable——一旦使用虚拟继承,“虚拟性”就会感染所有层级的所有派生类。跨度> 我在答案中添加了您之前的评论 - 因为对我来说,这是我真的不明白的一条信息。谢谢。

以上是关于当在其层次结构中具有菱形继承的类的多重继承时,函数的模糊继承的主要内容,如果未能解决你的问题,请参考以下文章

C++反汇编第四讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式.

py--多重继承

C++:继承(继承的概念和性质,赋值兼容规则,菱形继承和虚拟继承)

在未完成的类层次结构中的“最终”类上使用虚拟继承

Python菱形继承的初始化问题和继承顺序

菱形继承问题