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()
4

2 回答 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; }
};

如果没有虚拟继承,您的多重继承方案看起来就像从两个独立且不完整的基类继承,D1并且D2两者都不能被实例化。

于 2012-07-26T05:31:37.617 回答
3

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

是的。

每个Base基类子对象都带来两个纯虚函数。你想要多少这些基础子对象Deriv

  • 如果您想要 2 个Base基类子对象,Deriv::D1::Base并且Deriv::D2::Base(因此从Deriv&to 的转换Base&将是模棱两可的)那么您将在Deriv: Deriv::D1::Base::F1(), Deriv::D1::Base::F2(), Deriv::D2::Base::F1(),中拥有 4 个不同的虚函数Deriv::D2::Base::F2()。只实现了第一个和最后一个,所以中间的两个是纯虚拟的:Deriv::D1::Base::F2(), Deriv::D2::Base::F1(). 你有两个完全独立的继承关系:Deriv继承自D1Deriv继承自D2
  • 如果您只想要一个Base基类子对象Deriv::Base,那么您将只有 2 个不同的虚函数在Deriv: Base::F1(), Base::F2().

C++ 中的非虚拟继承是“具体的”继承,就像包含:struct D : B意味着对于每个D对象都只有一个B基类子对象,就像struct C { M m; }意味着对于每个对象C都只有一个M类成员子对象。这些关系是一对一的。

OTOH,虚拟继承是一个更“抽象”的东西:struct D : virtual B意味着对于每个D对象都与一个B基类子对象相关联,但这种关系是多对一的,例如struct C { M &m; }.

通常,对于任何派生类D(此处Deriv)和(此处)的任何虚拟基B类,虚拟派生自(此处D,)Base的所有基类都有助于覆盖基类中的虚函数:DBDeriv::D1Deriv::D2

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

非虚拟和虚拟继承是非常不同的继承关系,就像非虚拟成员函数和虚拟函数在派生类和基类中具有相同签名的函数之间引入不同的关系(隐藏与覆盖)。

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

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

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()虚函数签名(有 4 个虚函数Deriv,但只有 2 个不同的签名),所以您需要 2 个函数定义:

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

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

于 2012-07-26T05:58:54.627 回答