13

抽象インターフェースの部分実装をいくつか持ち、多重継承を使用してこれらの部分実装を 1つの具象クラスにまとめることができますか?

次のコード例があります。

#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; }
};

仮想継承がなければ、多重継承のシナリオは、インスタンス化できない 2 つの別個の不完全な基本クラスD1およびからの継承のように見えます。D2

于 2012-07-26T05:31:37.617 に答える
3

抽象インターフェースの部分実装をいくつか持ち、多重継承を使用してこれらの部分実装を 1 つの具象クラスにまとめることができますか?

はい。

Base基本クラスのサブオブジェクトは、2 つの純粋仮想関数をもたらします。これらのベース サブオブジェクトのうちいくつが に必要ですDerivか?

  • 2 つBaseの基本クラス サブオブジェクトが必要な場合 (したがって からDeriv::D1::BaseDeriv::D2::Baseの変換があいまいDeriv&になります) 、、 、、Base&に 4 つの異なる仮想関数がありDerivます。最初と最後のものだけが実装されているので、中間の 2 つのものは純粋な仮想です: , . 2 つの完全に独立した継承関係があります: inherits frominherits fromです。Deriv::D1::Base::F1()Deriv::D1::Base::F2()Deriv::D2::Base::F1()Deriv::D2::Base::F2()Deriv::D1::Base::F2()Deriv::D2::Base::F1()DerivD1DerivD2
  • Base基本クラス サブオブジェクトが1 つだけ必要な場合は、Deriv::Base次の 2 つの異なる仮想関数のみが必要Derivです。Base::F1()Base::F2()

C++ での非仮想継承は、コンテインメントのような「具体的な」継承です。struct D : Bつまり、オブジェクトごとDに 1 つのB基本クラス サブオブジェクトが存在することを意味し、ちょうど 1 つのクラス メンバー サブオブジェクトがstruct C { M m; }それぞれC存在することを意味します。Mこれらの関係は 1 対 1 です。

OTOH、仮想継承はより「抽象的」です。struct D : virtual Bつまり、各オブジェクトは基本クラスのサブオブジェクトにD関連付けられていますが、この関係は のように多対 1です。Bstruct C { M &m; }

一般に、任意の派生クラスD(here Deriv) および任意の仮想ベース(here ) の場合B、(here , )から仮想的に派生したすべての基本クラスは、基本クラスの仮想関数のオーバーライドに寄与します。DBaseDBDeriv::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 に答える