是必要的纯抽象类(接口)的虚拟继承

bpw*_*621 13 c++ inheritance multiple-inheritance virtual-inheritance

为什么在编译器下面的代码中抱怨这PureAbstractBase是一个模糊的基类MultiplyInheritedClass?我意识到我有两个PureAbstractBasein MultiplyInheritedClass和that的副本,FirstConreteClass并且SecondConreteClass应该实际上派生,因为它们是钻石的中间行(这确实解决了下面代码的问题).但即使我有两个接口副本,为什么代码中的代码MultiplyInheritedClass不仅仅覆盖它们并且明确地选择了定义的接口类MultiplyInheritedClass

#include <iostream>
using namespace std;

class PureAbstractBase {
  public:
    virtual void interface() = 0;
};

// I know that changing the following line to:
// class FirstConcreteClass : public virtual PureAbstractBase {
// fixes the problem with this hierarchy
class FirstConcreteClass : public PureAbstractBase {
  public:
    virtual void interface() { implementation(); }
  private:
    void implementation() { cout << "This is object FirstConcreteClass\n"; }
};

// I know that changing the following line to:
// class SecondConcreteClass : public virtual PureAbstractBase {
// fixes the problem with this hierarchy
class SecondConcreteClass : public PureAbstractBase {
  public:
    virtual void interface() { implementation(); }
  private:
    void implementation() { cout << "This is object SecondConcreteClass\n"; }
};

class MultiplyInheritedClass : public FirstConcreteClass,
                               public SecondConcreteClass {
  public:
    virtual void interface() { implementation(); }
  private:
    void implementation() { cout << "This is object MultiplyInheritedClass\n"; }
};
Run Code Online (Sandbox Code Playgroud)

此外,为什么我没有以下层次结构的问题?在这种情况下,ConcreteHandler类是否有三个AbstractTaggingInterface副本?那么为什么它与上面的例子没有相同的问题呢?

#include <iostream>
using namespace std;

class AbstractTaggingInterface {
  public:
    virtual void taggingInterface() = 0;
};

class FirstAbstractHandler : public AbstractTaggingInterface {
  public:
    virtual void taggingInterface() { cout << "FirstAbstractHandler\n"; }
    virtual void handleFirst() = 0;
};

class SecondAbstractHandler : public AbstractTaggingInterface {
  public:
    virtual void taggingInterface() { cout << "SecondAbstractHandler\n"; }
    virtual void handleSecond() = 0;
};

class ThirdAbstractHandler : public AbstractTaggingInterface {
  public:
    virtual void taggingInterface() { cout << "ThridAbstractHandler\n"; }
    virtual void handleThird() = 0;
};

class ConcreteHandler : public FirstAbstractHandler,
                        public SecondAbstractHandler,
                        public ThirdAbstractHandler {
  public:
    virtual void taggingInterface() = { cout << "ConcreteHandler\n"; }
    virtual void handleFirst() {}
    virtual void handleSecond() {}
    virtual void handleThird() {}
};
Run Code Online (Sandbox Code Playgroud)

我试图围绕这一切,因为我最近与一位同事进行了对话,他声称如果你继承了纯虚拟类(接口)而没有任何数据成员,那么就不需要虚拟继承.我认为理解为什么前一个代码示例不起作用而后者确实会在很长的路要走这一点(并清楚他的评论究竟是什么意思).提前致谢.

Ker*_* SB 5

您需要虚拟继承来克服钻石歧义:

class FirstConcreteClass  : public virtual PureAbstractBase { ... };
class SecondConcreteClass : public virtual PureAbstractBase { ... };
Run Code Online (Sandbox Code Playgroud)

冗长的解释:假设你有这个:

// *** Example with errrors! *** //
struct A { virtual int foo(); };
struct B1 : public A { virtual int foo(); };
struct B2 : public A { virtual int foo(); };
struct C: public B1, public B2 { /* ... */ };  // ambiguous base class A!

int main() {
  A * px = new C;                              // error, ambiguous base!
  px->foo();                                   // error, ambiguous override!
}
Run Code Online (Sandbox Code Playgroud)

虚函数的继承foo是模糊的,因为它有三种方式:from B1,from B2和from A.继承图形成一个"钻石":

   /-> B1 >-\
A->          ->C
   \-> B2 >-/
Run Code Online (Sandbox Code Playgroud)

通过使继承虚拟struct B1 : public virtual A;等,您允许任何基类C*调用正确的成员:

struct A { virtual int foo(); };
struct B1 : public virtual A { virtual int foo(); };
struct B2 : public virtual A { virtual int foo(); };
struct C: public B1, public B2 { virtual int foo(); };
Run Code Online (Sandbox Code Playgroud)

我们还必须C::foo()为此定义有意义,否则C将没有明确定义的成员foo.

更多细节:假设我们现在有一个正确的虚拟继承类C,如上所述.我们可以根据需要访问所有各种虚拟成员:

int main() {
  A * pa = new C;
  pa->foo();      // the most derived one
  pa->A::foo();   // the original A's foo

  B1 * pb1 = new C;
  pb1->foo();     // the most derived one
  pb1->A::foo();  // A's foo
  pb1->B1::foo(); // B1's foo

  C * pc = new C;
  pc->foo();      // the most derived one
  pc->A::foo();   // A's foo
  pc->B1::foo();  // B1's foo
  pc->B2::foo();  // B2's foo
  pc->C::foo();   // C's foo, same as "pc->foo()"
}
Run Code Online (Sandbox Code Playgroud)

 

更新:正如David在评论中所说,重要的一点是中间类B1B2实际上继承,以便进一步的类(在这种情况下C)可以继承它们,同时保持继承不A明确.抱歉最初的错误,谢谢你的纠正!

  • Downvoted(刚才,不是四个小时前),因为`MultiplyInheritedClass`是使用虚拟继承的错误位置.必须在`FirstConcreteClass`和`SecondConcreteClass`中使用虚拟继承.`MultiplyInheritedClass`应该使用简单的公共继承(除非它也可以用于其他一些元菱形模式). (2认同)

Bil*_*eal 5

您的第一个示例失败,因为编译器无法消除三个实现之间的歧义implementation().您正在覆盖该方法MultiplyInheritedClass,这实际上会覆盖FirstConcreteClass::implementationSecondConcreteClass::implementation(一次虚拟的,总是虚拟的).但是,两个虚拟呼叫仍然存在于接口中MultiplyInheritedClass,这使得呼叫在呼叫站点处不明确.

您的示例在没有virtual继承的情况下工作的原因是公共基类没有冲突的实现.换一种方式:

class Base
{
public:
    void DoSomething() {
    std::cout << "TADA!";
    }
}

class One : public Base
{
    //...
}

class Two : public Base
{
    //...
}

class Mixed : public One, public Two
{
    //...
}

int main()
{
    Mixed abc;
    abc.DoSomething(); //Fails because the compiler doesn't know whether to call
                       // One::DoSomething or Two::DoSomething, because they both
                       // have implementations.

    //In response to comment:
    abc.One::DoSomething(); //Succeeds! You removed the ambiguity.
}
Run Code Online (Sandbox Code Playgroud)

因为您的示例具有所有纯虚函数,所以编译器无需消除歧义的多个实现.因此,只存在一个实现,并且调用是明确的.