为什么编译器在下面的代码中抱怨PureAbstractBase
是MultiplyInheritedClass
的不明确基类?我意识到我在MultiplyInheritedClass
中有两个PureAbstractBase
的副本,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 FirstConcreteClassn"; }
};
// 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 SecondConcreteClassn"; }
};
class MultiplyInheritedClass : public FirstConcreteClass,
public SecondConcreteClass {
public:
virtual void interface() { implementation(); }
private:
void implementation() { cout << "This is object MultiplyInheritedClassn"; }
};
此外,为什么我对以下层次结构没有问题?在这种情况下,ConcreteHandler类不是有三个AbstractTaggingInterface副本吗?那么,为什么它没有和上面的例子一样的问题呢?
#include <iostream>
using namespace std;
class AbstractTaggingInterface {
public:
virtual void taggingInterface() = 0;
};
class FirstAbstractHandler : public AbstractTaggingInterface {
public:
virtual void taggingInterface() { cout << "FirstAbstractHandlern"; }
virtual void handleFirst() = 0;
};
class SecondAbstractHandler : public AbstractTaggingInterface {
public:
virtual void taggingInterface() { cout << "SecondAbstractHandlern"; }
virtual void handleSecond() = 0;
};
class ThirdAbstractHandler : public AbstractTaggingInterface {
public:
virtual void taggingInterface() { cout << "ThridAbstractHandlern"; }
virtual void handleThird() = 0;
};
class ConcreteHandler : public FirstAbstractHandler,
public SecondAbstractHandler,
public ThirdAbstractHandler {
public:
virtual void taggingInterface() = { cout << "ConcreteHandlern"; }
virtual void handleFirst() {}
virtual void handleSecond() {}
virtual void handleThird() {}
};
我正试图理解这一切,因为我最近与一位同事进行了一次对话,他声称如果你从没有任何数据成员的纯虚拟类(接口)继承,那么虚拟继承就没有必要了。我认为,理解为什么前一个代码示例不起作用,而后一个起作用,将大大有助于我理清思路(并澄清他的评论到底是什么意思)。提前谢谢。
您需要虚拟继承来克服菱形歧义:
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
、来自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在评论中所说,这里的要点是中间类B1
和B2
实际上是继承的,这样更多的类(在本例中为C
)可以从继承它们,同时保持从A
的继承不含糊。对最初的错误表示歉意,并感谢您的更正!
您的第一个例子失败了,因为编译器无法在implementation()
的三个实现之间消除歧义。您正在MultiplyInheritedClass
中重写该方法,它实际上同时重写FirstConcreteClass::implementation
和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.
}
因为您的示例具有所有纯虚拟函数,所以编译器不需要消除多个实现的歧义。因此,只有一个实现存在,并且调用是明确的。
我尝试了这两个问题代码,它们在实例化多继承类的对象时运行良好。它不仅适用于多态性,例如:
PureAbstractBase* F;
F = new MultiplyInheritedClass();
原因很清楚:它不知道应该链接到抽象基类的哪个副本(很抱歉表达式不正确,我理解这个想法,但无法表达)。由于固有virtaully使得派生类中只存在一个副本,所以这很好。
此外,Billy ONeal的代码也不清楚,我们应该放置什么而不是注释?
如果我们放置:
public:
void DoSomething()
{ std::cout << "TADA!"; }
它运行良好,因为没有虚拟性。
我在Visual Studio 2008上工作。
为什么不这样做(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
的实例调用overriden函数 - 通过两阶段转换消除歧义