从多个抽象基类的部分实现中继承?

13

是否可以有多个抽象接口的部分实现,然后通过使用多重继承将这些部分实现收集到一个具体类中?

我有以下示例代码:

#include <iostream>

struct Base
{
    virtual void F1() = 0;
    virtual void F2() = 0;
};

struct D1 : Base
{
    void F1() override { std::cout << __func__ << std::endl; }
};

struct D2 : Base
{
    void F2() override { std::cout << __func__ << std::endl; }
};

// collection of the two partial implementations to form the concrete implementation
struct Deriv : D1, D2
{
    using D1::F1; // I added these using clauses when it first didn't compile - they don't help
    using D2::F2;
};

int main()
{
    Deriv d;
    return 0;
}

以下是编译错误信息:

main.cpp: In function ‘int main()’:
main.cpp:27:11: error: cannot declare variable ‘d’ to be of abstract type ‘Deriv’
main.cpp:19:8: note:   because the following virtual functions are pure within ‘Deriv’:
main.cpp:5:18: note:    virtual void Base::F1()
main.cpp:6:18: note:    virtual void Base::F2()

1
+1 对于简短、自包含的示例。 - juanchopanza
一般而言,“抽象接口”意味着虚拟继承(特殊情况可能有所不同)。 - curiousguy
1
struct D1 : Base” 这里为什么要使用公有继承?D1 对客户端代码有用处吗? - curiousguy
@curiousguy 没有原因 - 只是举了个例子 - 在我的特定使用中,我根据情况使用公共和私有的混合。 - Steve Lorimer
2个回答

10
尝试从Base中实现虚拟继承
struct D1 : virtual Base
{
    void F1() override { std::cout << __func__ << std::endl; }
};

struct D2 : virtual Base
{
    void F2() override { std::cout << __func__ << std::endl; }
};

没有虚拟继承,你的多重继承场景看起来像是从两个不完整的基类 D1D2 继承,它们都不能被实例化。

不完整的基类是指抽象基类吗? - curiousguy
@juanchopanza 什么是抽象类? - curiousguy
@curiousguy 哦!我明白你的意思了。我仍然很难把一个有许多实现方法的类称为“抽象类”,但我应该尊重C++术语。 - juanchopanza
@curiousguy 当然,我见过没有任何实现方法的抽象类。 - juanchopanza
1
在这种情况下,我收回所有观点。 - juanchopanza
显示剩余3条评论

3
是否可以有多个抽象接口的部分实现,然后通过使用多重继承将这些部分实现收集到一个具体类中?
是的。
每个Base基类子对象带来两个纯虚函数。你想在Deriv中有多少个这样的基类子对象?
- 如果您想要2个Base基类子对象,即Deriv::D1::Base和Deriv::D2::Base(因此从Deriv&到Base&的转换将是模棱两可的),则在Deriv中将有4个不同的虚函数:Deriv::D1::Base::F1()、Deriv::D1::Base::F2()、Deriv::D2::Base::F1()、Deriv::D2::Base::F2()。只有第一个和最后一个被实现了,所以中间两个是纯虚的:Deriv::D1::Base::F2()、Deriv::D2::Base::F1()。你有两个完全独立的继承关系:Deriv从D1继承,Deriv从D2继承。 - 如果您只想要一个Base基类子对象Deriv::Base,则在Deriv中只有2个不同的虚函数:Base::F1()、Base::F2()。
C++中的非虚拟继承是“具体”的继承,就像包含一样:struct D : B意味着对于每个D对象,恰好有一个B基类子对象,就像struct C { M m; }意味着对于每个C,恰好有一个M类成员子对象。这些关系是一对一的。
另一方面,虚继承是更加“抽象”的:struct D : virtual B意味着对于每个D对象都与一个B基类子对象相关联,但这种关系是多对一的,就像struct C { M &m; }。
一般来说,对于任何派生类 D(此处为 Deriv),以及 D 的任何虚基类 B(此处为 Base),D 的所有基类,都从 B 虚拟继承(此处为 Deriv::D1Deriv::D2),都会对基类中的虚函数覆盖做出贡献:

  • Base::F1()Deriv::D1::F1() 覆盖
  • Base::F2()Deriv::D2::F2() 覆盖

非虚继承和虚继承是非常不同的继承关系,就像非虚成员函数和虚函数在派生类和基类之间引入不同的函数关系一样(隐藏 vs. 覆盖)。

与虚函数和非虚函数一样,虚基类和非虚基类必须在不同情况下使用(在一般情况下,一个不比另一个“更好”)。

如何在没有虚继承的情况下“修复”代码?

struct Deriv : D1, D2
{
    using D1::F1; // I added these using clauses when it first didn't compile - they don't help
    using D2::F2;
};

是的:使用声明控制名称查找,因此它们会影响可见性和歧义问题,而不涉及虚函数覆盖。

针对原始的非虚拟继承设计,为了使Deriv成为一个具体的类,您必须显式实现F1()F2()虚函数签名(在Deriv中有4个虚函数,但只有2个不同的签名),因此需要2个函数定义:

struct Deriv : D1, D2
{
    void F1() override { D1::F1(); }
    void F2() override { D2::F2(); }
};

请注意,Deriv::F1() 覆盖了 Deriv::D1::F1()Deriv::D2::F1()


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