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

Inherit from multiple partial implementations of an abstract base class?

本文关键字:实现 继承 抽象 基类      更新时间:2023-10-16

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

我有以下示例代码:

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

尝试从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继承,这两个基类都无法实例化。

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

是的。

每个Base基类子对象都带来两个纯虚函数。您希望在Deriv中有多少个基本子对象?

  • 如果你想要 2 个Base基类子对象、Deriv::D1::BaseDeriv::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继承自D1Deriv继承自D2
  • 如果你只需要一个Base基类子对象Deriv::Base,那么你在Deriv中将只有 2 个不同的虚函数: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(,以及D的任何虚拟基B(此处Base(,所有虚拟派生自B D的基类(此处Deriv::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()虚函数签名(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()