我希望有一个接口IA和另一个扩展它的IB。
然后实现IA,B继承A并实现IB。
然而,当编译B得到错误时,说IA内容是未定义的,即使A定义了所有内容:(
class IA
{
public:
virtual ~IA(){}
virtual void foo()=0;
};
class IB : public IA
{
public:
virtual void bar()=0;
};
class A : public IA
{
public:
A();
void foo();
};
class B : public A, public IB
{
public:
B();
void bar();
};
错误C2259:'B':无法实例化抽象类
由于以下成员:
'void IA :: foo(void)':是抽象的
答案 0 :(得分:17)
从以下几点开始,查看C ++ FAQ:https://isocpp.org/wiki/faq/multiple-inheritance#mi-diamond
它详细解释了“可怕的钻石”和虚拟继承。
答案 1 :(得分:6)
(不,A
没有定义任何内容。您宣布 A()
和void foo()
,但您没有定义他们。)
真正的问题是你的多重继承。
B
/ \
A IB
/ \
IA IA
正如您所看到的,您的继承树中有IA
的两个“版本”,只有A
实现了void IA::foo
(但是,如上所述,这会给你您没有定义实现时出现链接器错误。
void IB::IA::foo()
仍然没有实现;因此,B
本身“继承了抽象性”,你无法实例化它。
B
也必须实现void foo()
,或者您可以使用虚拟继承来破解它。
答案 2 :(得分:5)
这是 真正完全合理的虚拟继承案例,并不总是一种设计气味。
在并行层次结构接口/实现的情况下,“any class - > interface”形式的每个继承关系都应标记为virtual:class A : public virtual IA
; class B : public virtual IB, public A
和class IB : public virtual IA
。
这种方式,可能(依赖于实现但至少在概念上),在每个虚拟派生类中存储额外的指针以知道它应该在何处找到其虚拟基础。类A
,B
和IB
将指向IA
,而类B
将指向IB
。每个虚拟基地也都有自己的vtable。
“可怕的钻石”的观点在图形上很好,但重点是派生类应该知道它们将使用的基类所在的位置,这就是虚拟继承所在的位置。你显然需要一个vtable每个接口在这里,虚拟继承允许你这样做。
然后你可以继续并行,并添加额外的课程C
,IC
,等等,请记住:
每当你继承一个接口时,都会虚拟地继承。
顺便说一下,COM类有类似的设计。
答案 3 :(得分:1)
你有两个的foo()副本,一个从IB :: IA继承,一个从A :: IA继承。其中只有一个在A中具有非抽象版本。