oop — C ++: объединение, агрегация и состав

Я начинаю изучать OOAD, и мне трудно найти C++ пример кода, который иллюстрирует, как Association, Aggregation а также Composition реализованы программно. (Есть несколько постов везде, но они относятся к C # или Java). Я нашел пример или два, но все они противоречат инструкциям моего инструктора, и я запутался.

Я понимаю, что в:

  • Ассоциация: Foo имеет указатель на объект Bar в качестве члена данных
  • Агрегация: Foo имеет указатель на объект Bar, и данные Bar глубоко копируются в этот указатель.
  • Состав: Foo имеет объект Bar в качестве члена данных.

И вот как я это реализовал:

//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;
}
};

Это правильно? Если нет, то как это сделать вместо этого? Буду признателен, если вы также дадите мне ссылку на код из книги (чтобы я мог обсудить это с моим инструктором)

21

Решение

Я собираюсь игнорировать агрегацию. это не очень четко определенная концепция и на мой взгляд, это вызывает больше путаницы, чем оно того стоит. Композиции и ассоциации вполне достаточно, Крейг Ларман сказал мне так. Возможно, это не тот ответ, который искал ваш инструктор, но вряд ли он будет реализован в 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 деструктор, хотя в противном случае у вас утечка памяти (или используйте умный указатель).

14

Другие решения


По вопросам рекламы [email protected]