Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Langage C++
  3. Associations entre classes
Extrait - Langage C++ De l'héritage C au C++ moderne (avec programmes d'illustration) (2e édition)
Extraits du livre
Langage C++ De l'héritage C au C++ moderne (avec programmes d'illustration) (2e édition) Revenir à la page d'achat du livre

Associations entre classes

Introduction

Un aspect important de la programmation objet est de mettre en relation des objets, de les relier entre eux. Plusieurs options techniques sont envisageables. L’idée, en s’appuyant sur des définitions UML de base, est d’en amorcer l’exploration et l’expérimentation. Il s’agit de stimuler recherche et intuition en vue d’implémentations pertinentes de relations entre objets dans un récit applicatif.

Principes des associations pour les relations entre objets

Des classes peuvent être interdépendantes et des objets peuvent communiquer, c’est-à-dire échanger des informations sous forme de valeurs. C’est ce qu’en UML on appelle des associations. UML (Unified Modeling Language) est un langage pour la modélisation souvent utilisé en amont de la programmation objet dont il reprend les grands principes et clarifie les possibilités d’interprétation. Il reconnaît essentiellement trois degrés d’associations avec trois moyens de les implémenter.

1. Association simple

Des objets séparés s’échangent des valeurs. On dit qu’ils s’envoient des messages. Pour ce faire, le mieux est d’utiliser des méthodes avec en paramètre des valeurs de propriétés d’objets, des objets ou des références d’objets. En UML, c’est ce que l’on appelle une association simple. Le lien est alors faible entre deux classes et consiste uniquement en un échange de valeurs considérées comme des « messages ».

2. Agrégation

Des objets contiennent des pointeurs sur des objets existants distinctement. Le lien entre deux objets est alors plus fort que dans une association simple : un objet pointe sur un autre ou peut même en contenir un autre. Cependant, l’interdépendance qui les unit n’est pas complète parce que la disparition de l’un n’entraîne pas nécessairement la disparition de l’autre. La disparition de l’objet pointeur n’entraîne pas la disparition de l’objet pointé. En UML, c’est ce que l’on appelle une agrégation. Par exemple un humain et son téléphone portable : la disparition du portable n’entraîne...

Associations simples : messages entre objets

1. Liaison non réciproque entre deux objets

Un objet peut transmettre de l’information sous forme de valeurs à un autre objet via simplement des paramètres de fonctions. Traduisons par exemple en C++ une princesse qui mange une pomme empoisonnée. Nous avons un objet de la classe pomme qui contient une dose de poison et un objet de la classe princesse qui peut croquer une pomme. Selon la dose de poison, la santé de la princesse se trouve altérée, et une méthode de la classe princesse permet de connaître l’état de la princesse.

Pour la pomme, nous avons :

class pomme 
{ 
private: 
    int poison; 
public : 
    pomme(){ poison = rand() % 25; } 
    inline int GetPoison()const{ return poison; } 
}; 

La quantité de poison est stockée dans une variable privée et elle est définie aléatoirement à la déclaration d’un objet pomme dans la fourchette de 0 à 25.

La princesse est définie de la façon suivante :

class princesse 
{ 
private: 
    int sante; 
public: 
    princesse() { sante = rand() % 100; } *
    void croque(int poison) { sante -= poison; } 
    void croque(pomme p) { sante -= p.GetPoison(); } 
    //void croque(const pomme &p){ sante -= p.GetPoison(); } 
    void etat(); 
}; 
void princesse::etat() 
{ 
    if (sante < 0) 
        std::cout << "princesse morte : "; 
    else...

Agrégations : coopération entre objets

Un pointeur d’objet en propriété signifie que dans la relation à l’objet pointé, l’objet pointé peut varier ; l’objet pointé est interchangeable. Nous l’avons déjà mis en œuvre avec la relation Avion-Pilote du chapitre Pointeurs, utilisations classiques. La liaison est à sens unique si l’objet pointé ne pointe pas à son tour sur celui qui le pointe. Elle est réciproque dans le cas contraire. Techniquement, le principe est simple. Mais ce qui est important, c’est l’interprétation que l’on peut en faire dans le scénario et le récit d’une application.

1. Liaison à sens unique (exemple guitare, guitariste)

Un guitariste joue de la guitare. Il en possède une et il peut en changer, mais la guitare ne connaît pas le guitariste. Voici un programme possible :

#include <iostream> 
#include <string> 
 
class Guitare 
{ 
private: 
    std::string type; 
public: 
    Guitare(std::string type) : type(type) {} 
    void affiche() { std::cout << type << '\n'; } 
}; 
 
class Guitariste 
{ 
private: 
    Guitare* guitare; 
public: 
    Guitariste() :guitare(nullptr) {} 
    void ChangeGuitare(Guitare* g) 
    { 
        this->guitare = g; 
        this->guitare->affiche(); 
    } 
}; 
 
int main() 
{ 
    Guitariste Alain; 
    Guitare...

Liaisons réciproques entre objets

Dans une liaison réciproque, les deux objets se connaissent, chacun dispose d’un pointeur qui pointe vers l’autre. C’est simple, mais cela pose quelques problèmes à la compilation.

1. Problème de syntaxe

Soit deux classes A et B qui contiennent chacune un pointeur qui pointe vers l’autre :

class A 
{ 
public : 
   B*b; 
}; 
 
class B 
{ 
public : 
   A*a; 
 
}; 

À la compilation, nous obtenons ce genre d’erreurs :

error C2143: erreur de syntaxe : absence de ';' avant '*' 
error C4430: spécificateur de type manquant 

2. Déclaration de type incomplet

En fait, B n’est pas connu avant A et c’est le pointeur B*b de A qui pose problème. Pour cette situation, le C++ fournit un moyen : une déclaration de type incomplet sous la forme :

class B ;  // ou struct B dans le cas d'une structure 

qu’il suffit d’ajouter avant la déclaration de A, ce qui donne :

class B; 
class A 
{ 
public : 
    B*b; 
}; 
 
class B 
{ 
public : 
    A*a; 
 
}; 

Dans le main(), nous déclarons un objet b de la classe B et un objet a de la classe A, puis nous marions b et a ; b prend l’adresse de a et a prend l’adresse de b.

int main() 
{ 
   B b; 
   A a; 
   b.a = &a; 
   a.b = &b; 
 
 
   return 0; 
} 

3. Limite du type incomplet

Le type de B est incomplet pour A, c’est-à-dire que A reconnaît le type B, mais ignore son contenu en mémoire. Alors tout fonctionne tant...

Composition : dépendance entre objets

Soit A et B deux objets liés entre eux. Dans une composition, si l’un des deux disparaît, l’autre disparaît aussi, comme un animal est détruit s’il perd sa tête. À la différence d’une agrégation, où deux objets continuent d’exister indépendamment l’un de l’autre, le lien entre des objets constitutifs d’une composition est très resserré. Une certaine permanence est assurée dans la relation qui unit les objets entre eux. L’objectif de l’implémentation est alors de limiter la volatilité dans les positionnements d’objets entre eux.

1. Choisir entre agrégation ou composition

La relation entre une voiture et son moteur illustre un lien resserré entre deux objets. La voiture possède un moteur, et si le moteur casse, la voiture est hors d’usage. À ce moment-là, soit le scénario applicatif permet le changement de moteur et nous sommes au sens strict dans une agrégation, soit il ne le permet pas et nous sommes alors dans une composition.

Par exemple, pendant une course de voitures, si le moteur d’une voiture lâche, la course est finie pour elle et nous sommes dans une composition parce qu’il n’est pas possible de changer le moteur de la voiture pendant la course. En revanche, dans une casse, toutes les pièces de la voiture peuvent être vendues et certaines réutilisées. Il s’agirait là plutôt d’une agrégation.

2. Techniques envisageables

Dans le cadre d’une composition, des pointeurs gérés spécifiquement peuvent s’utiliser. On peut également placer des objets en dur dans une classe. Une bonne solution consiste à utiliser des références d’objets, notamment...