C ++で多重継承を回避する必要があるのはなぜですか?

ハイ

多重継承を使用するのは良い概念ですか、それとも代わりに他のことを行うことができますか?

paercebal

多重継承(MIと略される)はにおいがします。これは、通常、それが悪い理由で行われたことを意味し、メンテナの前で吹き返します。

概要

  1. 継承ではなく、機能の構成を検討する
  2. 恐怖のダイヤモンドに注意してください
  3. オブジェクトではなく、複数のインターフェースの継承を検討してください
  4. 時々、多重継承は正しいことです。そうである場合は、それを使用します。
  5. コードレビューで複数継承されたアーキテクチャを守る準備をしてください

1.おそらく構成?

これは継承にも当てはまります。したがって、多重継承にも当てはまります。

あなたのオブジェクトは本当に別のものから継承する必要がありますか?ACarEngine、からも​​、からも継承する必要はありませんWheelACarにはEngineと4つありWheelます。

構成の代わりに多重継承を使用してこれらの問題を解決する場合は、何か問題があります。

2.恐怖のダイヤモンド

通常、あなたはクラス持ちA、その後、BCの両方から継承しますAそして(理由を聞かないでください)そして誰かがそれDからBとの両方を継承なければならないと決定しCます。

私はこの種の問題に88年に2回遭遇しましたが、次の理由で見るのはおもしろいです。

  1. これは悪いアーキテクチャだったので(実際には、まったく存在してならなかったのですが...)、最初からどれだけの間違いでしたか(どちらの場合も、とのD両方から継承すべきではありませんでした)。BCC
  2. C ++では、親クラスAが孫クラスに2回存在していたため、メンテナがいくら払っていたDので、1つの親フィールドA::fieldを更新するということは、それを2回更新するか(とを介しB::fieldC::field)、後で何かが黙って間違ってクラッシュすることを意味しました。(でポインタを新しくしB::field、削除しC::fieldます...)

C ++でキーワードvirtualを使用して継承を修飾すると、これが必要なものでない場合は上記の二重レイアウトを回避できますが、とにかく、私の経験では、おそらく何か間違ったことをしています...

オブジェクト階層では、階層をグラフではなくツリー(ノードの親が1つ)として保持するようにしてください。

ダイヤモンドの詳細(2017-05-03編集)

C ++でのDiamondof Dreadの本当の問題(デザインが健全であると仮定して-コードをレビューしてください!)は、選択を行う必要があることです

  • クラスAがレイアウトに2回存在することが望ましいですか、それはどういう意味ですか?はいの場合は、必ず2回継承します。
  • 一度だけ存在する必要がある場合は、仮想的に継承します。

この選択は問題に固有のものであり、C ++では、他の言語とは異なり、言語レベルで設計を強制することなく実際に行うことができます。

しかし、すべての力と同様に、その力には責任が伴います。設計をレビューしてもらいます。

3.インターフェース

上記のDiamondof Dreadに遭遇しないため、0個または1個の具象クラスの多重継承、および0個以上のインターフェースは通常問題ありません。実際、これがJavaで行われる方法です。

通常、Cが継承しABユーザーがCそれがであるかのようにA、および/またはであるかのように使用できることを意味しますB

C ++では、インターフェイスは次のような抽象クラスです。

  1. そのすべてのメソッドは純粋仮想(= 0でサフィックス)と宣言 されました(2017-05-03を削除)
  2. メンバー変数なし

ゼロから1つの実オブジェクトへの多重継承、およびゼロ以上のインターフェースは「臭い」とは見なされません(少なくともそれほど多くはありません)。

C ++抽象インターフェースの詳細(2017-05-03を編集)

まず、実際の基準は状態を持たない(つまり、を除いてメンバー変数を持たない)ことであるため、NVIパターンを使用してインターフェイスを作成できますthisあなたの抽象的なインターフェースのポイントは、契約を公開することです(「あなたは私をこのように、そしてこのように呼ぶことができます」)、それ以上でもそれ以下でもありません。抽象仮想メソッドのみを持つことの制限は、義務ではなく、設計上の選択であるべきです。

第2に、C ++では、(追加のコスト/間接参照があっても)抽象インターフェースから仮想的に継承することは理にかなっています。そうしないと、インターフェイスの継承が階層内に複数回表示されるため、あいまいさが生じます。

第三には、オブジェクト指向は素晴らしいですが、それはないだけ真実そこTM C ++で。適切なツールを使用し、さまざまな種類のソリューションを提供するC ++の他のパラダイムがあることを常に忘れないでください。

4.本当に多重継承が必要ですか?

時々、はい。

通常、あなたのCクラスから継承しているABし、AそしてB2つの無関係なオブジェクト(すなわちないなどと同じ階層に、共通して何も、異なる概念、中)です。

たとえば、NodesX、Y、Z座標を使用して、多くの幾何学的計算(おそらく点、幾何学的オブジェクトの一部)を実行できるシステムがあり、各ノードは自動エージェントであり、他のエージェントと通信できます。

おそらく、あなたはすでに2つのライブラリ、独自の名前空間を持つ各へのアクセス持っている(名前空間を使用する別の理由を...しかし、あなたは、あなたの名前空間をしていないでしょう?)、1ビーイングgeoや他のビーイングをai

したがってown::Nodeai::Agentとの両方から独自の派生物がありますgeo::Point

これは、代わりにコンポジションを使用すべきではないかどうかを自問する必要がある瞬間です。own::Nodeが本当にaai::Agentとaの両方である場合geo::Point、合成は機能しません。

次にown::Node、3D空間での位置に応じて他のエージェントと通信する、多重継承が必要になります

ai::Agentそしてgeo::Point、完全に、完全に、完全に無関係であることに気付くでしょう...これは多重継承の危険性を劇的に減らします)

その他の場合(2017-05-03を編集)

他の場合があります:

  • 実装の詳細として(できればプライベートな)継承を使用する
  • ポリシーのような一部のC ++イディオムは、多重継承を使用できます(各部分が他の部分と通信する必要がある場合this
  • std :: exceptionからの仮想継承(例外には仮想継承が必要ですか?

コンポジションを使用できる場合もあれば、MIの方が優れている場合もあります。重要なのは、あなたには選択肢があるということです。責任を持って実行してください(そしてコードをレビューしてもらいます)。

5.では、多重継承を行う必要がありますか?

ほとんどの場合、私の経験では、違います。MIは、機能しているように見えても、適切なツールではありません。怠惰な人が、結果を認識せずに機能を積み上げるために使用できるためです(CaranEngineとaの両方を作成するなどWheel)。

しかし時々、はい。そしてその時、MIほどうまくいくものはありません。

しかし、MIは臭いので、コードレビューでアーキテクチャを防御する準備をしてください(防御できない場合は防御すべきではないため、防御することは良いことです)。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

C#を学ぶ必要があるのはなぜですか?

分類Dev

JavaまたはC#で多重継承が許可されないのはなぜですか?

分類Dev

C ++で多重継承のあいまいさを解決する方法

分類Dev

C#のほとんどの型がSystem.Objectから継承されるのはなぜですか?

分類Dev

C ++で使用する前に文字列を初期化する必要があるのはなぜですか?

分類Dev

C / C ++で配列へのポインタを使用する必要があるのはなぜですか?

分類Dev

Cでポインターへの参照を*で定義する必要があるのはなぜですか?

分類Dev

C#がCOMDLLを参照するために登録する必要があるのはなぜですか?

分類Dev

C#での継承は、「抽象、外部、または部分としてマークされていないため、本文を宣言する必要があります」

分類Dev

C#で==と!=の両方を定義する必要があるのはなぜですか?

分類Dev

このC ++テンプレートでタイプを指定する必要があるのはなぜですか?

分類Dev

2次元配列。Cで列のサイズを宣言する必要があるのはなぜですか

分類Dev

コンテナ内でbash -l -cを使用する必要があるのはなぜですか?

分類Dev

C ++クラス定義でメソッドを宣言する必要があるのはなぜですか?

分類Dev

Objective-cで@propertyキーワードを使用する必要があるのはなぜですか?

分類Dev

C ++ビットフィールドでタイプを指定する必要があるのはなぜですか?

分類Dev

C ++標準で `Clock :: now`関数を` static`にする必要があるのはなぜですか?

分類Dev

C#で.EqualsとGetHashCodeをオーバーライドする必要があるのはなぜですか

分類Dev

C ++ヘッダーファイルで「#ifdefinedIdentifier」を使用する必要があるのはなぜですか?

分類Dev

なぜ、a.out自体ではなく "sh -c a.out"を実行する必要があるのでしょうか。

分類Dev

OSごとにC / C ++を再コンパイルする必要があるのはなぜですか?

分類Dev

異なるパラメーターを持つ継承されたメンバーがある場合、C ++構造体の関数の呼び出しがあいまいになるのはなぜですか?

分類Dev

「C」が「B」からパブリックに継承し、Bが「A」からプライベートに継承する場合、「C」内に「A」のオブジェクトを作成できないのはなぜですか?

分類Dev

SwiftでCスタイルの/ * ... * /コメントを避ける必要があるのはなぜですか?

分類Dev

C、なぜ printf をループの外側ではなく内側に置く必要があるのですか?

分類Dev

C#の「WebPageExecutingBase.Href()」メソッドを使用する必要があるのはいつ/なぜですか?

分類Dev

空のコンストラクタc ++を定義する必要があるのはなぜですか

分類Dev

C ++-多重継承で派生クラスタイプをチェックすることは可能ですか?

分類Dev

列挙型変数を宣言する必要があるのはなぜですか?(C)

Related 関連記事

  1. 1

    C#を学ぶ必要があるのはなぜですか?

  2. 2

    JavaまたはC#で多重継承が許可されないのはなぜですか?

  3. 3

    C ++で多重継承のあいまいさを解決する方法

  4. 4

    C#のほとんどの型がSystem.Objectから継承されるのはなぜですか?

  5. 5

    C ++で使用する前に文字列を初期化する必要があるのはなぜですか?

  6. 6

    C / C ++で配列へのポインタを使用する必要があるのはなぜですか?

  7. 7

    Cでポインターへの参照を*で定義する必要があるのはなぜですか?

  8. 8

    C#がCOMDLLを参照するために登録する必要があるのはなぜですか?

  9. 9

    C#での継承は、「抽象、外部、または部分としてマークされていないため、本文を宣言する必要があります」

  10. 10

    C#で==と!=の両方を定義する必要があるのはなぜですか?

  11. 11

    このC ++テンプレートでタイプを指定する必要があるのはなぜですか?

  12. 12

    2次元配列。Cで列のサイズを宣言する必要があるのはなぜですか

  13. 13

    コンテナ内でbash -l -cを使用する必要があるのはなぜですか?

  14. 14

    C ++クラス定義でメソッドを宣言する必要があるのはなぜですか?

  15. 15

    Objective-cで@propertyキーワードを使用する必要があるのはなぜですか?

  16. 16

    C ++ビットフィールドでタイプを指定する必要があるのはなぜですか?

  17. 17

    C ++標準で `Clock :: now`関数を` static`にする必要があるのはなぜですか?

  18. 18

    C#で.EqualsとGetHashCodeをオーバーライドする必要があるのはなぜですか

  19. 19

    C ++ヘッダーファイルで「#ifdefinedIdentifier」を使用する必要があるのはなぜですか?

  20. 20

    なぜ、a.out自体ではなく "sh -c a.out"を実行する必要があるのでしょうか。

  21. 21

    OSごとにC / C ++を再コンパイルする必要があるのはなぜですか?

  22. 22

    異なるパラメーターを持つ継承されたメンバーがある場合、C ++構造体の関数の呼び出しがあいまいになるのはなぜですか?

  23. 23

    「C」が「B」からパブリックに継承し、Bが「A」からプライベートに継承する場合、「C」内に「A」のオブジェクトを作成できないのはなぜですか?

  24. 24

    SwiftでCスタイルの/ * ... * /コメントを避ける必要があるのはなぜですか?

  25. 25

    C、なぜ printf をループの外側ではなく内側に置く必要があるのですか?

  26. 26

    C#の「WebPageExecutingBase.Href()」メソッドを使用する必要があるのはいつ/なぜですか?

  27. 27

    空のコンストラクタc ++を定義する必要があるのはなぜですか

  28. 28

    C ++-多重継承で派生クラスタイプをチェックすることは可能ですか?

  29. 29

    列挙型変数を宣言する必要があるのはなぜですか?(C)

ホットタグ

アーカイブ