Я начинаю изучать OOAD, и мне трудно найти C++
пример кода, который иллюстрирует, как Association
, Aggregation
а также Composition
реализованы программно. (Есть несколько постов везде, но они относятся к C # или Java). Я нашел пример или два, но все они противоречат инструкциям моего инструктора, и я запутался.
Я понимаю, что в:
И вот как я это реализовал:
//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;
}
};
Это правильно? Если нет, то как это сделать вместо этого? Буду признателен, если вы также дадите мне ссылку на код из книги (чтобы я мог обсудить это с моим инструктором)
Я собираюсь игнорировать агрегацию. это не очень четко определенная концепция и на мой взгляд, это вызывает больше путаницы, чем оно того стоит. Композиции и ассоциации вполне достаточно, Крейг Ларман сказал мне так. Возможно, это не тот ответ, который искал ваш инструктор, но вряд ли он будет реализован в C ++ иначе.
Существует не один способ реализации композиции и ассоциации. То, как вы их реализуете, будет зависеть от ваших требований, например, от множества отношений.
Простейшим способом реализации композиции является использование простого Bar
переменная-член так же, как вы предложили. Единственное изменение, которое я хотел бы сделать, это инициализировать bar
в списке инициализатора члена конструктора:
// COMPOSITION - with simple member variable
class Foo {
private:
Bar bar;
public:
Foo(int baz) : bar(baz) {}
};
Как правило, рекомендуется инициализировать переменные-члены с помощью списка инициализации конструктора, это может быть быстрее, а в некоторых случаях, например, для переменных-членов const, это единственный способ их инициализировать.
Также может быть причина для реализации композиции с использованием указателя. Например Bar
может быть полиморфным типом, и вы не знаете конкретный тип во время компиляции. Или, возможно, вы хотите, чтобы объявить вперед Bar
минимизировать зависимости компиляции (см. PIMPL идиома). Или, возможно, кратность этого отношения составляет от 1 до 0..1, и вы должны иметь возможность иметь нулевое значение 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)) {}
};
Я использовал std::unique_ptr
здесь, потому что Foo
является единственным владельцем Bar
но вы можете использовать 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
но я думаю, что это должно быть последним средством.
Относительно вашей реализации Агрегации с глубокой копией Bar
указатель. Я бы не сказал, что это типичная реализация, но, как я уже сказал, это зависит от ваших требований.
Вы должны убедиться, что вы звоните delete
на ваше bar
указатель члена в Foo
деструктор, хотя в противном случае у вас утечка памяти (или используйте умный указатель).