4

多重継承を使用したことはありませんが、最近それについて読んでいるときに、コード内で実際にどのように使用できるかを考え始めました。私が通常ポリモーフィズムを使用するとき、私は通常、次のような基本クラスのポインタとして宣言された新しい派生インスタンスを作成することによってそれを使用します。

 BaseClass* pObject = new DerivedClass();

派生クラスで仮想関数を呼び出すときに正しいポリモーフィックな動作が得られるようにします。このようにして、仮想関数を介して動作に関して自分自身を管理するさまざまなポリモーフィックタイプのコレクションを作成できます。

多重継承の使用を検討するとき、私は同じアプローチを考えていましたが、次の階層がある場合、これをどのように行うのでしょうか?

 class A {
     virtual void foo() = 0;
 };
 class B : public A {
     virtual void foo() {
         // implementation
     }
 };

 class C {
     virtual void foo2() = 0;
 };
 class D : public C {
     virtual void foo2() {
         // implementation
     }
 };

 class E : public C, public B {
     virtual void foo() {
         // implementation
     }
     virtual void foo2() {
         // implementation
     }
 };

この階層を使用して、クラスEの新しいインスタンスを次のように作成できます。

 A* myObject = new E();

また

 C* myObject = new E();

また

 E* myObject = new E();

しかし、それをtheとして宣言するとA*、クラスCとDの継承階層のポリモーフィズムが失われます。同様に、それを宣言するとC*、クラスAとBのポリモーフィズムが失われます。そのように宣言するとE*、オブジェクトは基本クラスポインターを介してアクセスされないため、通常の方法でポリモーフィックな動作を取得できません。

だから私の質問はこれに対する解決策は何ですか?C ++は、これらの問題を回避できるメカニズムを提供しますか、それとも基本クラス間でポインター型を前後にキャストする必要がありますか?確かに、私は直接次のことを行うことができなかったので、これはかなり面倒です

 A* myA = new E();
 C* myC = dynamic_cast<C*>(myA);

キャストはNULLポインタを返すためです。

4

4 に答える 4

7

多重継承を使用すると、複数の異なる方法で表示できる単一のオブジェクトが得られます。たとえば、次のように考えてください。

class door { 
   virtual void open();
   virtual void close();
};

class wood { 
    virtual void burn();
    virtual void warp();
};

class wooden_door : public wood, public door {
    void open() { /* ... */ }
    void close() { /* ... */ }
    void burn() { /* ... */ }
    void warp() { /* ... */ }
};

ここで、 wooden_door オブジェクトを作成すると、それをドア オブジェクト (への参照またはポインタ) を処理することを期待する関数、またはドア オブジェクト (へのポインタまたは参照) を処理することを期待する関数に渡すことができます。wood物体。

確かに、多重継承によって、動作する関数に新しい機能が突然与えられるわけではありません (またはその逆)。私たちが期待しているのは、木製のドアを開閉できるドアとして、または燃えたり歪んだりできる木片として扱うことができることです。それがまさに私たちが得たものです。doorwood

于 2012-08-02T07:18:04.103 に答える
3

この場合、クラスACはインターフェイスであり、E2 つのインターフェイスを実装します。C(通常、中間クラスはあり ませんがD、そのような場合です。)これを処理するにはいくつかの方法があります。

最も頻繁に行われるのは、おそらく と の合計である新しいインターフェイスを定義することAですC

class AandC : public A, public C {};

Eこれから派生します。その後、通常は をE介し​​て 管理しAandC*、 を受け取る関数に無関心に渡しA*ます C*。同じオブジェクトで両方のインターフェイスを必要とする関数は、AandC*.

インターフェイスAとインターフェイスCが何らかの形で関連している場合、たとえば、C一部の (ただしすべてではない) がサポートしたい追加機能を提供している場合、(オブジェクトがそうでない場合は null ポインターを返す関数を用意することはA理にかなっています。インターフェイスをサポートします)。AgetB()C*C

最後に、ミックスイン複数のインターフェイスがある場合、最もクリーンな解決策は、2 つの独立した階層を維持することです。1 つはインターフェイス用で、もう 1 つは実装部分用です。

//   Interface...
class AandC : public virtual A, public virtual C {};

class B : public virtual A
{
    //  implement A...
};

class D : public virtual C
{
    //  implement C...
};

class E : public AandC, private B, private D
{
    //  may not need any additional implementation!
};

(設計の観点からは、インターフェースの継承は常に仮想的であるべきであり、将来この種のことを可能にするために、たとえ今は必要でなくても、と言いたくなる.しかし、実際には、この種の使用を事前に予測できないことはめったにありません。)

この種のことについてさらに情報が必要な場合は、Barton と Nackman を読みたいと思うかもしれません。この本はかなり古いものですが (C++98 より前について説明しています)、ほとんどの情報はまだ有効です。

于 2012-08-02T08:07:03.680 に答える
1

これはうまくいくはずです

 A* myA = new E();
 E* myC = dynamic_cast<E*>(myA);
 myC->Foo2();
于 2012-08-02T07:15:46.373 に答える
0

C;Aではないため、にキャストできません。またはAにのみキャストできます。DE

を使用A*するE*と、常に次のようなことを明示的に言うことができますが、オーバーライドがあるかどうかに関係なく関数を暗黙的に呼び出すC::foo()方法はありません。AC

このような奇妙なケースでは、テンプレートは、クラスが共通の継承を持っていなくても、あたかも持っているかのように振る舞わせることができるので、しばしば良い解決策です。たとえば、foo2()呼び出すことができるものなら何でも動作するテンプレートを作成できます。

于 2012-08-02T07:16:11.600 に答える