是否需要从纯抽象类(接口)进行虚拟继承

发布于 2024-11-15 22:39:01 字数 2874 浏览 5 评论 0原文

为什么在下面的代码中编译器会抱怨 PureAbstractBaseMultiplyInheritedClass 的不明确基类?我意识到我在 MultiplyInheritedClass 中有两个 PureAbstractBase 副本,并且 FirstConreteClassSecondConreteClass 应该虚拟地派生,因为它们是菱形的中间行(这确实解决了下面代码的问题)。但是,即使我有接口的两个副本,为什么 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() {}
};

我试图理解所有这些,因为我最近与一位同事进行了一次对话,他声称如果您从没有任何数据成员的纯虚拟类(接口)继承,则不需要虚拟继承。我认为理解为什么前一个代码示例不起作用而后者起作用将有助于在我的脑海中弄清楚这一点(并弄清楚他的评论的确切含义)。提前致谢。

Why is it that in the code below the compiler complains that PureAbstractBase is an ambiguous base class of MultiplyInheritedClass? I realize I have two copies of the PureAbstractBase in MultiplyInheritedClass and that FirstConreteClass and SecondConreteClass should be derived virtually because they're the middle row of the diamond (and that does indeed fix the problem with the code below). But even though I have two copies of the interface why is it that the code in MultiplyInheritedClass does not just override both and unambiguously pick the interface class defined in 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"; }
};

Further, why do I not have issues with the following hierarchy? Doesn't the ConcreteHandler class have three copies of the AbstractTaggingInterface in this case? So why doesn't it have the same issue as the example above?

#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() {}
};

I am trying to wrap my head around all of this because I had a conversation with a colleague recently where he claimed that if you were inheriting from pure virtual classes (interfaces) without any data members then virtual inheritance was not necessary. I think understanding why the former code example does not work and the latter does would go a long way to getting this straight in my head (and clear up what exactly he meant by his comment). Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

裂开嘴轻声笑有多痛 2024-11-22 22:39:01

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

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 的继承是不明确的,因为它以三种方式出现:来自 B1< /code>,来自 B2 和来自 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()"
}

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

You need virtual inheritance to overcome the diamond-ambiguity:

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

Long-winded explanation: Suppose you have this:

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

The inheritance of the virtual function foo is ambiguous because it comes in three ways: from B1, from B2 and from A. The inheritance diagram forms a "diamond":

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

By making the inheritance virtual, struct B1 : public virtual A; etc., you allow any baseclass of C* to call the correct member:

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

We must also define C::foo() for this to make sense, as otherwise C would not have a well-defined member foo.

Some more details: Suppose we now have a properly virtually-inheriting class C as above. We can access all the various virtual members as desired:

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()"
}

 

Update: As David says in the comment, the important point here is that the intermediate classes B1 and B2 inherit virtually so that further classes (in this case C) can inherit from them while simultaneously keeping the inheritance from A unambiguous. Sorry for the initial mistake and thanks for the correction!

口干舌燥 2024-11-22 22:39:01

您的第一个示例失败了,因为编译器无法消除 implementation() 的三个实现之间的歧义。您正在重写 MultiplyInheritedClass 中的该方法,该方法实际上重写了 FirstConcreteClass::implementation 和 SecondConcreteClass::implementation (一旦虚拟,始终虚拟)。但是,这两个虚拟调用仍然存在于 MultiplyInheritedClass 的接口中,这使得调用在调用点变得不明确。

您的示例在没有虚拟继承的情况下工作的原因是公共基类的实现不存在冲突。换句话说:

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.
}

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

Your first example fails because the compiler cannot disambiguate between the three implementations of implementation(). You are overriding that method in MultiplyInheritedClass, which actually overrides both FirstConcreteClass::implementation and SecondConcreteClass::implementation (once virtual, always virtual). However, both virtual calls still exist in the interface of MultiplyInheritedClass, which makes the call ambiguous at the call site.

The reason that your example works without virtual inheritance is that there is no conflicting implementation of the common base class. Put another way:

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.
}

Because your example has all pure virtual functions, there's no multiple implementations which the compiler needs to disambiguate. Therefore, only one implementation exists, and the call is unambiguous.

笑饮青盏花 2024-11-22 22:39:01

我尝试了这两个问题代码,它们在实例化多继承类的对象时工作得很好。它不仅仅适用于多态性,例如:

PureAbstractBase* F;
F = new MultiplyInheritedClass();

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

而且Billy ONeal的代码根本不清楚,我们应该用什么来代替注释呢?

如果我们放置:

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

它工作得很好,因为没有虚拟性。

我使用 Visual Studio 2008。

I tried both of the question codes and they worked fine when instantiating an object of the multi-inherited class. It didn't work only with polymorphism, like this for example:

PureAbstractBase* F;
F = new MultiplyInheritedClass();

And the reason is clear: it doesn't know to which copy of the Abstract base class it should be linked (sorry for bad expressions, I understand the idea but can't express it). And since inherting virtaully makes only one copy exist in the derived class, then it's fine.

Also the code of Billy ONeal is not clear at all, what should we place instead of the comments?

If we place:

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

it works fine, because of no virtuality.

I work on Visual Studio 2008.

回梦 2024-11-22 22:39:01

为什么不这样做(在 中建议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歧义
  • 通过两阶段转换消除了

Why not do it like this (suggested in Benjamin Supnik's blog entry):

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

which gives:

Multiple
Multiple
Multiple    

This way:

  • no virtual bases are involved (do you really need them?)
  • you can call the overriden function via a an instance of the MultiplyInheritedClass
  • ambiguity is removed by a two-stage conversion
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文