12

为什么在下面的代码中编译器抱怨这PureAbstractBase是一个模棱两可的基类MultiplyInheritedClassPureAbstractBase我意识到我有两个inMultiplyInheritedClass和 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"; }
};

此外,为什么我对以下层次结构没有问题?在这种情况下,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() {}
};

我正试图将所有这些都包裹起来,因为我最近与一位同事进行了交谈,他声称如果您从没有任何数据成员的纯虚拟类(接口)继承,则不需要虚拟继承。我认为理解为什么前一个代码示例不起作用而后者确实会在我脑海中直截了当(并​​弄清楚他的评论到底是什么意思)。提前致谢。

4

4 回答 4

5

您需要虚拟继承来克服菱形歧义:

class FirstConcreteClass  : public virtual PureAbstractBase { ... };
class SecondConcreteClass : public virtual PureAbstractBase { ... };

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

// *** 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!
}

虚函数的继承foo是模棱两可的,因为它来自三种方式: from B1, fromB2和 from A。继承图形成一个“钻石”:

   /-> B1 >-\
A->          ->C
   \-> B2 >-/

通过使继承虚拟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(); };

我们还必须对此进行定义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()"
}

 

更新:正如大卫在评论中所说,这里的重点是中间类B1B2虚拟继承,以便进一步的类(在这种情况下C)可以从它们继承,同时保持继承的A明确性。很抱歉最初的错误,感谢您的更正!

于 2011-06-19T17:14:05.567 回答
5

您的第一个示例失败,因为编译器无法消除implementation(). 您正在覆盖该方法 in MultiplyInheritedClass,它实际上覆盖了FirstConcreteClass::implementationand SecondConcreteClass::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.
}

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

于 2011-06-19T17:32:59.933 回答
1

我尝试了两个问题代码,并且在实例化多继承类的对象时它们运行良好。它不仅仅适用于多态性,例如:

PureAbstractBase* F;
F = new MultiplyInheritedClass();

原因很清楚:它不知道应该链接到抽象基类的哪个副本(抱歉表达不好,我理解这个想法但无法表达)。而且由于虚拟继承使得派生类中只存在一个副本,所以没关系。

此外,Billy ONeal 的代码根本不清楚,我们应该放置什么而不是注释?

如果我们放置:

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

它工作正常,因为没有虚拟性。

我在 Visual Studio 2008 上工作。

于 2011-06-19T18:43:24.607 回答
0

为什么不这样做(在Benjamin Supnik 的博客条目中建议):

#include <iostream>

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

class FirstConcreteClass : public PureAbstractBase {
public:
    virtual void interface() { implementation(); }
private:
    void implementation() { std::cout << "Fisrt" << std::endl; }
};

class SecondConcreteClass : public PureAbstractBase {
public:
    virtual void interface() { implementation(); }
private:
    void implementation() { std::cout << "Second" << std::endl; }
};

class MultiplyInheritedClass : public FirstConcreteClass,
                               public SecondConcreteClass 
{
public:
    virtual void interface() { implementation(); }
private:
    void implementation() { std::cout << "Multiple" << std::endl; }
};

int main() {
MultiplyInheritedClass mic;
mic.interface();

FirstConcreteClass *fc = &mic; //disambiguate to FirstConcreteClass 
PureAbstractBase *pab1 = fc;
pab1->interface();

SecondConcreteClass *sc = &mic; //disambiguate to SecondConcreteClass 
PureAbstractBase *pab2 = sc;
pab2->interface();
}

这使:

Multiple
Multiple
Multiple    

这边走:

  • 不涉及虚拟基地(你真的需要它们吗?)
  • 您可以通过一个实例调用覆盖的函数MultiplyInheritedClass
  • 通过两阶段转换消除歧义
于 2012-01-23T13:08:31.637 回答