C++: Association, agrégation et Composition

je commence à étudier OOAD et j'ai de la difficulté à trouver un exemple de code C++ qui illustrerait comment Association , Aggregation et Composition sont mis en œuvre de façon programmatique. (Il y a plusieurs messages partout mais ils se rapportent à C# ou java). J'ai trouvé un ou deux exemples, mais ils entrent tous en conflit avec les instructions de mon instructeur et je suis confus.

ma compréhension est que dans:

  • Association: Foo a un pointeur sur la Barre de l'objet en tant que membre de données
  • agrégation: Foo a un pointeur vers L'objet de barre et les données de barre est copié en profondeur dans ce pointeur.
  • Composition: Foo a une Barre d'objet en tant que membre de données.

et voici comment je l'ai mis en œuvre:

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

est-ce exact? Si non, alors comment doit - il être fait à la place? Il serait apprécié si vous me donnez également une référence d'un code d'un livre (afin que je puisse discuter avec mon instructeur)

21
demandé sur user3841111 2014-09-11 08:59:40

1 réponses

je vais ignorer l'Agrégation. Il est pas un concept très clairement défini et à mon avis, il provoque plus de confusion qu'il ne vaut la peine. Composition et d'Association sont tout à fait assez, Craig Larman qui me l'a dit. Ce n'est peut-être pas la réponse que votre instructeur cherchait, mais il est peu probable qu'elle soit implémentée en C++ de toute façon.

il n'y a pas une seule façon de mettre en œuvre la Composition et L'Association. Comment vous mettre en œuvre dépendra de vos besoins, par exemple la multiplicité de la relation.

Composition

la façon la plus simple de mettre en œuvre la composition est d'utiliser une simple variable membre Bar un peu comme vous l'avez suggéré. Le seul changement que je ferais serait d'initialiser le bar dans la liste d'initialiseur de membre de constructeur:

// COMPOSITION - with simple member variable
class Foo {
 private:
  Bar bar;
 public:   
  Foo(int baz) : bar(baz) {}
};

C'est généralement une bonne idée d'initialiser membre variables en utilisant la liste d'initialisation du constructeur, elle peut être plus rapide et dans certains cas comme les variables membres de const, c'est la seule façon de les initialiser.

il pourrait aussi y avoir une raison pour mettre en œuvre la composition à l'aide d'un pointeur. Par exemple Bar pourrait être un type polymorphe et vous ne connaissez pas le type de béton au moment de la compilation. Ou peut-être voulez-vous transmettre déclarer Bar pour minimiser les dépendances de compilation (voir pimpl idiom ). Ou peut-être que la multiplicité de cette relation est de 1 à 0..1 et vous devez être en mesure d'avoir un null Bar . Bien sûr, parce que C'est la Composition Foo devrait posséder le Bar et dans le monde moderne de C++11/C++14 nous préférons utiliser des pointeurs intelligents au lieu de posséder des pointeurs bruts:

 // COMPOSITION - with unique_ptr
class Foo {
 private:
  std::unique_ptr<Bar> bar;
 public:   
  Foo(int baz) : bar(barFactory(baz)) {}
};

j'ai utilisé std::unique_ptr car Foo est l'unique propriétaire de Bar , mais vous pourriez vouloir utiliser std::shared_ptr si un autre objet a besoin d'un std::weak_ptr à Bar .

Association

L'Association serait habituellement implémentée en utilisant un pointeur comme vous l'avez fait:

// Association - with non-owning raw pointer
class Foo {
 private:
  Bar* bar;
 public:   
  void setBar(Bar* b) { bar = b; }
};

bien sûr, vous devez être sûr que Bar sera vivant tandis que Foo est l'utilisation sinon vous avez un pointeur pendante. Si la durée de vie de Bar est moins claire, alors un std::weak_ptr peut être plus approprié:

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

Maintenant Foo peut utiliser le Bar sans crainte d'être laissé avec un bancales pointeur:

 Foo foo;
 {
   auto bar = std::make_shared<Bar>(3);
   foo.setBar(bar);
   foo.useBar(); // ok
 }  
 foo.useBar(); // bar has gone but it is ok

dans certains cas où la propriété de Bar n'est pas vraiment claire, une Association pourrait être mise en œuvre en utilisant un std::shared_ptr , mais je pense que ce devrait être un dernier recours.

concernant votre implémentation D'agrégation avec une copie profonde d'un pointeur Bar . Je n'aurais pas dit que c'était une implémentation typique mais, comme je l'ai dit, cela dépend de vos exigences. Vous devez vous assurer d'appeler delete sur votre pointeur de membre bar dans le destructeur Foo bien qu'autrement vous ayez une fuite de mémoire (ou utilisez un pointeur intelligent).

14
répondu Chris Drew 2018-04-25 08:13:29