C++のプログラミングでは、オブジェクトを統合するために集約と合成の2つの方法がよく使用されます。これらの概念はクラス間の異なる関係を反映し、関連するオブジェクトのライフサイクルや破棄の責任に影響を与えます。
背景:
オブジェクト指向設計では、オブジェクト間の依存関係を分離することが常に重要でした。オブジェクト指向言語(Smalltalk、C++、Java)が登場するにつれて、「部分 - 全体」の関係をどのようにモデル化するかという問題が浮上しました。C++では、メモリ管理とオブジェクトのライフサイクルを手動で管理する必要があるため、これが特に重要になりました。
問題:
集約と合成の間の誤った選択は、メモリリークやリソースの重複、オブジェクトの破棄エラーを引き起こす可能性があります。また、これらの概念が混同されることがよくあります。
解決策:
コード例:
// 合成: class Engine {}; class Car { Engine engine; // EngineはCarと一緒に作成され、破棄されます }; // 集約: class Person {}; class Team { std::vector<Person*> members; // Personオブジェクトを指し、所有しません };
主な特徴:
メンバークラスにオブジェクトへのポインタがある場合、常に集約ですか?
いいえ!クラスがこのポインタを所有している場合(例えば、std::unique_ptrを通じて)、それは依然として合成です。関係の種類はフィールドのタイプではなく、ライフサイクルに対する責任によって決まります。
class House { std::unique_ptr<Room> room; // 合成、HouseはRoomを所有します };
合成は参照や生ポインタを通じて実現できますか?
できますが、それはオブジェクトが所有者によって作成され破棄され、参照またはポインタが最適化のために使用される場合のみです。しかし、所有権を明確に表現するために、値としてのオブジェクトやスマートポインタを使用する方がはるかに良いです。
合成において、部分オブジェクトが所有者外で作成され、所有者に渡された場合はどうなりますか?
この場合、合成の不変条件が破られる危険性があります:外部で作成されたオブジェクトが所有者に渡され、その所有者がそれを破棄し、どこかでそのオブジェクトへの参照が残っていると、ダングリングポインタが発生します。プロジェクト内で所有権と破棄に対する責任を厳密に定義する必要があります。
あるチームが、すべてのネストされたオブジェクトを生ポインタでコンテナに格納し、デストラクタで手動で破棄することに決めました。すべては、所有権のスキームを変更するまでうまくいきました。その結果、ポインタが二重に解放され、クラッシュが発生しました。
利点:
欠点:
別のチームは、実際に所有権の関係に対してstd::unique_ptrに切り替え、非所有権は一時的な参照の形でのみ使用しました。これにより、アーキテクチャが明確に表現されました。
利点:
欠点: