パブリック継承により、ベース クラスのコードを再利用できる派生クラスを使用して、IS-A 関係をモデル化できます。
それはよくある誤解です。継承のポイントは、基本クラスのコードを再利用できることではなく、さまざまな動作を提供する基本型を処理する既存のコードを活用することです。つまり、少なくとも OO 理論では。
C++ は強力で柔軟な言語であり、UML から言語への単一のマッピングはありません (これは、UML からのコード生成を期待している人々が何度も失敗する場所です。UML は細かい粒度の詳細の一部をキャプチャしません)。
一般化と実現は、C++ では継承0によって実装されますが、他の言語では異なります。例として、Java では、一般化は継承 ( extends
) によって実装され、実現はインターフェース実装 ( implements
) によって実装されます。
コンポジションこれはhas-a関係であり、通常はクラスの値メンバーを通じて実装されます。これが唯一の実装ではないことに注意してください1。
集計は、2 つのオブジェクトの相対的な有効期間や参照を別のオブジェクトにリセットできるかどうかなどの他の基準に応じて、ポインター2と参照メンバーを介して実装できます。
使用は明示的にモデル化されていませんが、発生します。使用関係は、インターフェイス (異なる型のオブジェクトを取得または返す関数がその型を使用する) または実装 (定義で異なる型を使用する関数 -- その型のオブジェクトをインスタンス化する) に存在する可能性があります。なんらかの目的) もこの型を使用します。これら 2 つをuseの異なるバリアントと見なす人もいます。最初のものは2 番目のものより厳密です。
最後に、テンプレートには他にも複数のオプションがあります。たとえば、言語には と の間に関係はありませんstd::vector<>::iterator
が、それらは言語のRandomAccessIteratorstd::deque<>::iterator
の概念をモデル化しており、 RandomAccessIteratorで動作するように設計されたテンプレートはそれらの両方を使用できます (たとえば、) の観点からどちらもランダム アクセス イテレータであり、その関係はその概念の一般化ですが、それはコードにはまったく存在しません (最終的に言語にいくつかのフレーバーの概念を追加することになる場合)。std::sort
std::sort
0)一般に、公開継承についてのみ話しますが、そうである必要はありません。別の型からパブリックに継承する型は、明らかに別の型/インターフェイスを一般化/*実現*していますが、これはプライベートな継承によっても発生する可能性があります。OO を教えるときに一般的に提示されないことの 1 つは、クラスに2 つの別個のインターフェースがあることです。一方で、あなたのタイプのユーザーがあなたのオブジェクトと対話するための公開インターフェースがあります。反対側には仮想がありますインターフェイス、つまり、型と動作を拡張する他の型との間のコントラクトです。C++ の一般的なイディオムは、NVI 非仮想インターフェイスです。これは、分離を強制することでこれを悪用しようとします。パブリック仮想関数がないということは、パブリック インターフェイスと仮想インターフェイスが完全に分離されていることを意味します。同様に、型 T は、基底型とのpublic is-a関係を持たない場合がありますが、内部的には基底型への参照またはポインターを他のサブシステムに渡すことができます。これらのサブシステムでは、型 Tはベースです。保護された継承は、動機となる使用例がなく役に立たないと見なされるため、無視できます。
1)場合によっては、他のニーズによって駆動され、ポインター型のメンバーを使用して実装できますが、それらは構築時に割り当てられ、囲んでいる型の破棄時に解放されます。場合によっては、継承を悪用して構成を行い、サイズの最適化 (空のベースの最適化) を実行します。たとえば、比較器を保持する代わりに、a は比較器std::map
から継承できます (SFINAE を使用して、比較器がファンクターであることを検出します)。コンパレーターのタイプに非常に頻繁な状態がない場合、コンパイラーはコンパレーターとその最初のメンバーをstd::map
同じメモリー位置に配置できます (つまり、コンパレーターはスペースを取りません)。
2)ポインターという用語は、スマート ポインターを含む一般的な意味で考えてください。