0

この質問は私の試験のリビジョンにあり、正しい軌道に乗っているかどうか疑問に思っています.C++の入門書からのものです。

UML モデルの関係は C++ でどのようにコーディングされますか?

パブリック継承により、ベース クラスのコードを再利用できる派生クラスを使用して、IS-A 関係をモデル化できます。もう 1 つのアプローチは、一方のオブジェクトが他方のオブジェクトを所有または保持するオブジェクト間の関係である包含を使用することです。これは HAS-A 関係をモデル化します。

例えば:

  • 車はモーターを持っているか所有しています
  • 車が作られたらモーターも作られる
  • 車が破壊されると、そのモーターも破壊されます。

    class Car 
    {
        private:
            Motor *motor;
        public:
            Car()
            { 
                 motor = new Motor(); 
            }
    
            ~Car() 
            { 
                 delete motor; 
            }
    };
    
4

3 に答える 3

2

C++ を使用すると、Java を使用するよりも構成関係を簡単に構築できます。最も単純なケースでは、含まれるオブジェクトは単なる値メンバーです。

class Car {
private:
  Motor motor;
  // No explicit construction/destruction required
};

AbstractMotor動的な型が依存性注入を使用して呼び出し元によって決定されるを含める場合は、さらに複雑になります。この場合の構成をモデル化するには、次を使用できますunique_ptr

class Car {
public:
  explicit Car(std::unique_ptr<AbstractMotor> motor): motor(std::move(motor)) { }
private:
  std::unique_ptr<AbstractMotor> motor;  // Still no explicit destruction required
};

unique_ptrCarあなたが の一意の所有者でありMotor、そのMotorライフタイムがオブジェクトにバインドされていることを保証しますCar

オブジェクトが別のオブジェクトを所有している場合は、生のポインターを使用しないようにしてください。ではunique_ptr、重要なデストラクタを実装したり、delete演算子を使用したりする必要はまったくありません。

于 2012-10-27T15:38:09.727 に答える
1

私はあなたの答えにあまり追加しません。

IS-A 関係に関する限り、パブリック継承は C++ のツールです (ただし、テンプレートを使用すると扱いにくくなり、常に期待どおりに機能するとは限りません)。HAS-A 関係に関しては、クラス メンバーが解決策です。正確に言うと、Motor motor(ポインターではなく) メンバーを使用します。これは、2 つの間の関係をさらに強調するためです。また、ポインターが である限りnull、メンバーは常に構築および破棄されます。

HTH、
エルメス

于 2012-10-27T15:24:03.513 に答える
0

パブリック継承により、ベース クラスのコードを再利用できる派生クラスを使用して、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::sortstd::sort


0)一般に、公開継承についてのみ話しますが、そうである必要はありません。別の型からパブリックに継承する型は、明らかに別の型/インターフェイスを一般化/*実現*していますが、これはプライベートな継承によっても発生する可能性があります。OO を教えるときに一般的に提示されないことの 1 つは、クラスに2 つの別個のインターフェースがあることです。一方で、あなたのタイプのユーザーがあなたのオブジェクトと対話するための公開インターフェースがあります。反対側には仮想がありますインターフェイス、つまり、型と動作を拡張する他の型との間のコントラクトです。C++ の一般的なイディオムは、NVI 非仮想インターフェイスです。これは、分離を強制することでこれを悪用しようとします。パブリック仮想関数がないということは、パブリック インターフェイスと仮想インターフェイスが完全に分離されていることを意味します。同様に、型 T は、基底型とのpublic is-a関係を持たない場合がありますが、内部的には基底型への参照またはポインターを他のサブシステムに渡すことができます。これらのサブシステムでは、型 Tベースです。保護された継承は、動機となる使用例がなく役に立たないと見なされるため、無視できます。

1)場合によっては、他のニーズによって駆動され、ポインター型のメンバーを使用して実装できますが、それらは構築時に割り当てられ、囲んでいる型の破棄時に解放されます。場合によっては、継承を悪用して構成を行い、サイズの最適化 (空のベースの最適化) を実行します。たとえば、比較器を保持する代わりに、a は比較器std::mapから継承できます (SFINAE を使用して、比較器がファンクターであることを検出します)。コンパレーターのタイプに非常に頻繁な状態がない場合、コンパイラーはコンパレーターとその最初のメンバーをstd::map同じメモリー位置に配置できます (つまり、コンパレーターはスペースを取りません)。

2)ポインターという用語は、スマート ポインターを含む一般的な意味で考えてください。

于 2012-10-27T17:26:11.290 に答える