OOADの勉強を始めましたが、C++
Association
、Aggregation
、およびComposition
をプログラムで実装する方法を示すコード例。 (どこにでもいくつかの投稿がありますが、それらはC#またはJavaに関連しています)。私は1つか2つの例を見つけましたが、それらはすべて私のインストラクターの指示と矛盾しており、私は混乱しています。
私の理解は:
そして、これは私がそれを実装した方法です:
//ASSOCIATION
class Bar
{
Baz baz;
};
class Foo
{
Bar* bar;
void setBar(Bar* _bar)
{
bar=_bar;
}
};
//AGGREGATION
class Bar
{
Baz baz;
};
class Foo
{
Bar* bar;
void setBar(Bar* _bar)
{
bar = new Bar;
bar->baz=_bar->baz;
}
};
//COMPOSTION
class Bar
{
Baz baz;
};
class Foo
{
Bar bar;
Foo(Baz baz)
{
bar.baz=baz;
}
};
これは正しいです?そうでない場合、代わりにどのように行う必要がありますか?本のコードの参照も教えていただければ幸いです(インストラクターと話し合うため)
集計を無視します。それは 非常に明確に定義された概念ではない であり、私の意見では、価値がある以上に混乱を引き起こします。作曲と連想は十分です Craig Larman そう言った。インストラクターが求めていた答えではないかもしれませんが、とにかく異なる方法でC++に実装することはほとんどありません。
構成と関連付けを実装する方法は1つではありません。それらの実装方法は、関係の多様性などの要件によって異なります。
構成を実装する最も簡単な方法は、あなたが提案したような単純なBar
メンバー変数を使用することです。私が行う唯一の変更は、コンストラクタメンバー初期化子リストのbar
を初期化することです。
// COMPOSITION - with simple member variable
class Foo {
private:
Bar bar;
public:
Foo(int baz) : bar(baz) {}
};
一般に、コンストラクターの初期化リストを使用してメンバー変数を初期化することをお勧めします。これはより高速であり、constメンバー変数のように初期化する唯一の方法です。
ポインタを使用して構成を実装する理由もあります。たとえば、Bar
は多相型であり、コンパイル時に具体的な型がわからないことがあります。または、コンパイルの依存関係を最小限に抑えるためにBar
を前方宣言することもできます( PIMPL idiom を参照)。または、この関係の多重度は1〜0..1であり、null Bar
を持つことができる必要があります。もちろん、これはコンポジションFoo
がBar
を所有する必要があるため、C++ 11/C++ 14の現代の世界では、生のポインターを所有するのではなく、スマートポインターを使用することを好みます。
// COMPOSITION - with unique_ptr
class Foo {
private:
std::unique_ptr<Bar> bar;
public:
Foo(int baz) : bar(barFactory(baz)) {}
};
Foo
がBar
の唯一の所有者であるため、ここでstd::unique_ptr
を使用しましたが、他のオブジェクトがstd::shared_ptr
を必要とする場合はstd::weak_ptr
を使用することができますBar
に。
関連付けは通常、あなたが行ったようにポインタを使用して実装されます。
// Association - with non-owning raw pointer
class Foo {
private:
Bar* bar;
public:
void setBar(Bar* b) { bar = b; }
};
もちろん、Bar
が使用している間にFoo
が生きていることを確信する必要があります。 Bar
のライフタイムがそれほど明確でない場合、std::weak_ptr
の方が適切かもしれません。
// Association - with weak pointer
class Foo {
private:
std::weak_ptr<Bar> bar;
public:
void setBar(std::weak_ptr<Bar> b) { bar = std::move(b); }
void useBar() {
auto b = bar.lock();
if (b)
std::cout << b->baz << "\n";
}
};
これで、Foo
は、ぶら下がりポインタが残ることを恐れずにBar
を使用できます。
Foo foo;
{
auto bar = std::make_shared<Bar>(3);
foo.setBar(bar);
foo.useBar(); // ok
}
foo.useBar(); // bar has gone but it is ok
Bar
の所有権が本当に不明な場合には、std::shared_ptr
を使用してAssociationを実装できますが、それは最後の手段であると思います。
Bar
ポインターのディープコピーを使用した集計の実装について。私はそれが典型的な実装だとは言わなかったでしょうが、私が言ったように、それはあなたの要件に依存します。 delete
デストラクタのbar
メンバーポインタでFoo
を呼び出すことを確認する必要がありますが、そうでない場合はメモリリークが発生します(またはスマートポインタを使用します)。