C++接口与继承

14

我想要有一个接口IA和另一个扩展它的接口IB。

然后A实现了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)' : 是一个抽象函数


@SteveJessop: 是哪个版本?4.1.2 拒绝它4.3.4 也一样 - Lightness Races in Orbit
使用 A:foo 似乎没有帮助。 - will
@Tomalak:我的main函数有一个打字错误(或者说是思维错误),我实例化了A而不是B - Steve Jessop
你必须使用虚继承。来自IA类的IB::foo()B类中未声明。A::foo()看起来相同,但实际上并不是。虚继承将“合并”这些函数。 - ali_bahoo
4个回答

17

+1 真是太慢了。我公司的门户代理服务器和内容检查软件真是太烦人了... - wheaties
2
@wheaties:这不是比赛。 :) - Lightness Races in Orbit
@will:使用一个不那么奇怪的继承树吧。 :) (躲开) - Lightness Races in Orbit
@will 请查看 https://dev59.com/HHRB5IYBdhLWcg3w6bYE - NPE
@will:C4250 是一个著名的微软“愚蠢警告”--可以使用 #pragma 安全地禁用它。 - Stuart Golodetz
显示剩余3条评论

6

(不,A并没有定义任何东西。你声明了A()void foo(),但是你没有定义它们。)

真正的问题在于你的多重继承。

       B
     /   \
   A      IB
  /         \
IA          IA

正如您所看到的,在您的继承树中有两个“版本”IA,只有A实现了void IA :: foo (尽管如我上面所述,这会给您带来链接器错误,因为您没有定义实现)。 void IB :: IA :: foo()仍未实现; 因此,B本身“继承了抽象性”,因此您无法实例化它。 B还必须实现void foo(),或者您可以使用虚拟继承来解决这个问题。

那不是问题。你不能实例化一个抽象类。这是C++标准。 - Adrian Marinica
@AdrianMar:是的,我知道什么是抽象类。我正在编辑我的答案。 :) - Lightness Races in Orbit
我明白了,那很抱歉。 :) 去掉“-”并添加“+”。 :) - Adrian Marinica

5

这是关于虚拟继承的真正充分理由,它并不总是一个设计上的问题。

在接口/实现的并行层次结构中,每个形如“任意类->接口”的继承关系都应该标记为虚拟继承:class A : public virtual IA; class B : public virtual IB, public Aclass IB : public virtual IA

这样,可能(取决于实现,但至少在概念上),每个虚拟派生类中都会存储一个额外的指针,以知道它应该在哪里找到其虚拟基类。类ABIB将有一个指向IA的指针,而类B将有一个指向IB的指针。每个虚拟基类也将有自己的虚函数表。

“可怕的菱形”观点在图形上很好看,但重点是派生类应该知道它们将使用的基类位于哪里,这就是虚拟继承发挥作用的地方。在这里,您显然需要每个接口一个虚函数表,而虚拟继承正是允许您这样做的。

然后,您可以继续并行,并添加额外的类CIC等,只需记住:

每当继承一个接口时,请使用虚拟继承。

顺便说一句,COM类具有类似的设计。


2
无论何时继承一个接口,都要使用虚拟继承。+1 - Jeffrey Faust

1

你有两个foo()的副本,一个来自IB::IA,另一个来自A::IA。只有其中一个在A中有一个非抽象版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接