【问题标题】:Inherit from multiple partial implementations of an abstract base class?从抽象基类的多个部分实现继承?
【发布时间】:2012-07-26 05:26:09
【问题描述】:

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

我有以下示例代码:

#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 表示简短、独立的示例。
  • "抽象接口" 概括地说:抽象接口意味着虚拟继承(特殊情况可能不同)
  • "struct D1 : Base " 你在这里使用公共继承是有原因的吗?客户端代码是否对 D1 感兴趣?
  • @curiousguy 没有理由 - 只是举了一个例子 - 在我的特殊用途中,我根据情况混合使用公共和私人

标签: c++ multiple-inheritance virtual-inheritance


【解决方案1】:

Base 尝试inheriting virtually

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 啊哈!我明白你的意思。如果实现的方法是“抽象的”,我仍然很难将具有一堆的类想像,但我应该尊重 C++ 行话。
  • @curiousguy 在那种情况下,我把它全部收回。
【解决方案2】:

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

是的。

每个Base 基类子对象带来两个纯虚函数。在Deriv 中你想要多少个基础子对象?

  • 如果你想要 2 个Base 基类子对象Deriv::D1::BaseDeriv::D2::Base(所以从Deriv&amp;Base&amp; 的转换会很模糊)那么你将在Deriv 中有4 个不同的虚函数:@ 987654329@、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; } 表示对于每个 @ 987654348@ 恰好有一个M 类成员子对象。这些关系是一对一的。

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

一般来说,对于任何派生类D(此处为Deriv),以及对于D(此处为Base)的任何虚拟基类BD 的所有基类都虚拟派生自B(这里是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() 虚函数签名(@987654371 中有 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()

【讨论】:

    猜你喜欢
    • 2013-08-25
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    • 1970-01-01
    • 2012-04-11
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多