Chapitre 6 – Composition et relation entre classe
Document réservé
Vous consultez actuellement la présentation publique de ce chapitre.
Les documents PDF complets, comprenant les développements théoriques, les exemples détaillés et les exercices, sont disponibles sur demande.
Pour obtenir un accès, contactez-moi via la page Contact en indiquant les domaines qui vous intéressent (C++, ESP-IDF, électronique, etc.).
6 Composition et relations entre classes
6.1 Une classe peut contenir des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1.1 Une relation différente de l’héritage . . . . . . . . . . . . . . . . . . . . . . . .
6.2.2 Utiliser l’objet membre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.3 Composition et héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.4 Construction des objets membres . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.5 Une première tentative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.6 La liste d’initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.7 Construire n’est pas affecter . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.8 Observer l’ordre de construction . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.9 Ordre de construction des objets membres . . . . . . . . . . . . . . . . . . . .
6.2.10 Qui décide de l’ordre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.11 Un ordre fixé par la classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.12 Respecter l’ordre de déclaration . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3 Ordre de destruction des objets membres . . . . . . . . . . . . . . . . . . . . . . . . .
6.3.1 Construction et destruction . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3.2 Le corps du destructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3.3 La destruction des membres est automatique . . . . . . . . . . . . . . . . . .
6.3.4 Un destructeur explicite n’est pas nécessaire . . . . . . . . . . . . . . . . . . .
6.4 Composition ou héritage ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.2 La relation « possède un » . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.3 Un héritage techniquement possible mais conceptuellement faux . . . . . . .
6.4.4 Un héritage techniquement possible mais conceptuellement faux . . . . . . .
6.4.5 Combiner héritage et composition . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.6 Construire des objets complexes par assemblage . . . . . . . . . . . . . . . . .
6.5 Plusieurs objets membres et délégation des tâches . . . . . . . . . . . . . . . . . . . .
6.5.1 Déléguer une responsabilité . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6 Composition et pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6.1 Deux situations différentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6.2 Un objet créé dynamiquement . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6.3 La responsabilité de la destruction . . . . . . . . . . . . . . . . . . . . . . . .
6.6.4 Accéder à l’objet pointé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6.5 Pointer vers un objet existant . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6.6 Une information absente du pointeur classique . . . . . . . . . . . . . . . . .
6.6.8 Un objet existant indépendamment . . . . . . . . . . . . . . . . . . . . . . . .
6.6.10 Le pointeur ne définit pas à lui seul la relation . . . . . . . . . . . . . . . . .
6.6.11 Utiliser une référence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2026 – Partie II C++
Composition et relations entre classe
Comparaison des relations entre classes . . . . . . . . . . . . . . . . . . . . . . . . . .
6.7.2 La composition : « possède un » . . . . . . . . . . . . . . . . . . . . . . . . .
6.7.3 L’agrégation : utiliser un objet indépendant . . . . . . . . . . . . . . . . . . .
6.7.4 Plusieurs relations dans une même classe . . . . . . . . . . . . . . . . . . . . .
6.7.5 Trois questions pour identifier la relation . . . . . . . . . . . . . . . . . . . . .
AccueilComposition et relations entre Une classe peut contenir des objets Jusqu’à présent, les membres de nos classes étaient souvent des objets appartenant aux types fondamentaux du C++. Nous avons par exemple utilisé : double partieReelle; double partieImaginaire; Mais un membre d’une classe peut lui-même être un objet appartenant à une autre classe. Considérons une classe représentant un moteur : Moteur(double puissanceInitiale); double getPuissance() const; Nous pouvons maintenant utiliser un objet de type Moteur comme membre d’une autre classe : Voiture(double puissance); 2026 – Partie II C++ Composition et relations entre classe Un objet de type Voiture contient donc réellement un objet de type Moteur. Nous pouvons représenter cette organisation de manière simplifiée : L’objet Moteur fait ici partie de l’objet Voiture. Cette relation entre classes porte le nom de composition. Une relation différente de l’héritage La composition ne doit pas être confondue avec l’héritage. L’héritage exprime généralement une un carré est une forme. Nous pouvons donc écrire : class Carre : public Forme La composition exprime au contraire une relation du type : Une voiture n’est pas un moteur. Une voiture possède un moteur. Nous écrivons donc : • Un membre d’une classe peut lui-même être un objet appartenant à une autre classe. • Lorsqu’un objet contient directement un autre objet comme l’un de ses membres, nous obtenons une relation de composition. • L’héritage exprime généralement une relation « est un ». • La composition exprime généralement une relation « possède un ».
Accueil2026 – Partie II C++ Composition et relations entre classe La relation « possède un » La composition permet de représenter une relation dans laquelle un objet est constitué, entre autres, d’un ou de plusieurs autres objets. Dans notre exemple, une voiture possède un moteur : Le membre moteur n’est pas un pointeur vers un objet Moteur situé ailleurs. Il s’agit véritablement d’un objet de type Moteur contenu dans l’objet de type Voiture. Considérons la création d’une voiture : Voiture voiture(…); L’objet voiture contient lui-même son membre moteur. Nous pouvons nous représenter très schématiquement la situation ainsi : Le moteur fait donc partie de l’état de la voiture. Puisque le moteur est lui-même un objet, il possède sa propre durée de vie. Il doit être construit lorsque la voiture est construite et il doit être détruit lorsque la voiture est détruite. La composition établit donc également une relation entre les durées de vie des deux objets. Utiliser l’objet membre Les méthodes de Voiture peuvent naturellement utiliser l’objet moteur qu’elle contient. Nous pouvons par exemple écrire : double Voiture::getPuissance() const return moteur.getPuissance(); La méthode getPuissance() de Voiture demande donc à son objet moteur de fournir sa puissance.
Accueil2026 – Partie II C++ Composition et relations entre classe voiture.getPuissance() moteur.getPuissance() La composition ne supprime pas l’encapsulation. La classe Voiture possède un objet Moteur, mais cela ne lui donne pas pour autant un accès particulier aux membres privés de la classe Moteur. Si puissance est privée dans Moteur, la classe Voiture doit utiliser l’interface publique de Moteur Composition et héritage La différence entre héritage et composition ne concerne pas seulement la syntaxe. class Carre : public Forme un objet Carre peut être considéré comme une forme particulière. Avec une composition : une voiture ne devient pas un moteur. Elle utilise simplement un moteur comme l’un des éléments • Dans une composition, l’objet membre fait réellement partie de l’objet qui le contient. • L’objet membre conserve néanmoins son propre type et son propre comportement. • La classe contenant un objet ne reçoit aucun accès particulier aux membres privés de cet objet. Elle utilise son interface publique. • Lorsque l’objet membre est directement contenu dans un autre objet, leurs durées de • La question « est un » conduit généralement vers l’héritage, tandis que la question « possède un » conduit généralement vers la composition.
Accueil2026 – Partie II C++ Composition et relations entre classe Construction des objets membres Notre classe Voiture contient directement un objet de type Moteur : Voiture(double puissance); Supposons maintenant que la classe Moteur possède le constructeur suivant : Moteur::Moteur(double puissanceInitiale) : puissance(puissanceInitiale) La création d’un objet Voiture pose alors une question : comment construire le moteur contenu dans la voiture ? Une première tentative Nous pourrions être tentés d’écrire : Voiture::Voiture(double puissance) moteur = Moteur(puissance); Cette écriture arrive cependant trop tard. Lorsque l’exécution entre dans le corps du constructeur de Voiture, tous les membres de l’objet doivent déjà avoir été construits. L’objet moteur doit donc exister avant l’exécution de : moteur = Moteur(puissance); La liste d’initialisation Le constructeur de Voiture doit indiquer comment construire son membre moteur. Pour cela, nous utilisons la liste d’initialisation : Voiture::Voiture(double puissance) demande au constructeur de Voiture de construire le membre moteur en appelant le constructeur de Moteur avec l’argument puissance.
Accueil2026 – Partie II C++ Composition et relations entre classe Construire n’est pas affecter Il faut distinguer deux opérations fondamentalement différentes. construit directement l’objet moteur avec la valeur souhaitée. En revanche, une écriture placée dans le corps du constructeur : moteur = Moteur(puissance); est une affectation. Pour pouvoir effectuer cette affectation, moteur devrait donc déjà avoir été construit auparavant. La liste d’initialisation permet de construire directement l’objet membre dans son état initial. Observer l’ordre de construction Nous pouvons ajouter temporairement un affichage dans les deux constructeurs. Voiture voiture(150.0); Construction du moteur Construction de la voiture Le résultat est logique : avant que l’objet Voiture puisse être considéré comme construit, tous les objets qui le composent doivent eux-mêmes avoir été construits. • Un objet membre doit être construit avant l’exécution du corps du constructeur de l’objet • La liste d’initialisation permet d’indiquer directement quel constructeur doit être utilisé pour construire un objet membre. Voiture::Voiture(double puissance) l’expression moteur(puissance) appelle le constructeur de Moteur. • Une initialisation et une affectation sont deux opérations différentes : l’affectation suppose que l’objet existe déjà. • Avant l’exécution du corps d’un constructeur, les objets membres ont déjà été construits. REPRENDRE à 17.8 COMPOSITIONS ET POINTEURS
Accueil2026 – Partie II C++ Composition et relations entre classe Ordre de construction des objets membres Une classe peut naturellement contenir plusieurs objets membres. Ajoutons à notre voiture une La classe Voiture contient maintenant deux objets : Voiture(double puissance); #include « Voiture.h » Voiture voiture(150.0); Construction du moteur Construction de la batterie Construction de la voiture Program ended with exit code: 0 Qui décide de l’ordre ? L’ordre de construction des objets membres n’est pas déterminé par leur ordre dans la liste d’initialisation. Il est déterminé par leur ordre de déclaration dans la classe.
Accueil2026 – Partie II C++ Composition et relations entre classe Voiture(double puissance); moteur est déclaré avant batterie. Le moteur sera donc toujours construit avant la batterie. Un ordre fixé par la classe Cette règle garantit que l’ordre de construction des membres est toujours le même, quel que soit le constructeur utilisé. L’ordre est donc déterminé une seule fois par la déclaration des membres dans la classe. Ainsi : signifie que moteur sera construit avant batterie dans tous les constructeurs de Voiture. Respecter l’ordre de déclaration Même si l’ordre écrit dans la liste d’initialisation ne détermine pas l’ordre réel de construction, il est fortement recommandé d’utiliser le même ordre que celui des déclarations. On préférera donc : Voiture::Voiture(double puissance) : moteur(puissance), si les membres ont été déclarés dans cet ordre. Le code reflète alors directement l’ordre réel des opérations. • Les objets membres sont construits avant l’exécution du corps du constructeur de l’objet • Lorsque plusieurs objets membres sont présents, leur ordre de construction est déterminé par leur ordre de déclaration dans la classe. • L’ordre écrit dans la liste d’initialisation ne modifie pas cet ordre de construction. • Il est donc recommandé d’écrire la liste d’initialisation dans le même ordre que les déclarations des membres. • Cette règle garantit un ordre de construction unique et prévisible, quel que soit le constructeur utilisé. Ordre de destruction des objets membres Nous avons constaté que les objets membres sont construits avant l’objet qui les contient.
Accueil2026 – Partie II C++ Composition et relations entre classe La destruction suit la logique inverse. Pour observer ce mécanisme, ajoutons temporairement un destructeur à chacune de nos classes. Construction et destruction L’ordre de destruction est l’inverse de l’ordre de construction. Dans notre exemple, les membres de Voiture sont déclarés dans l’ordre : La construction s’effectue donc selon l’ordre : Moteur −→ Batterie −→ Voiture Lors de la destruction, l’ordre est inversé : Voiture −→ Batterie −→ Moteur Le corps du destructeur Lorsqu’un objet Voiture est détruit, le corps de son destructeur est exécuté en premier : << « Destruction de la voiture » Une fois ce corps exécuté, C++ détruit automatiquement les objets membres, dans l’ordre inverse de leur construction. Ainsi, batterie est détruite avant moteur. La destruction des membres est automatique Le destructeur de Voiture n’a pas à appeler explicitement les destructeurs de Moteur et de Batterie. C++ détruit automatiquement les objets membres lorsque l’objet qui les contient arrive en fin de vie. Cette propriété constitue l’un des grands intérêts de la composition : la durée de vie des objets directement contenus est naturellement liée à celle de l’objet qui les contient. Un destructeur explicite n’est pas nécessaire Dans notre exemple, nous avons écrit explicitement les destructeurs afin d’observer l’ordre des opérations. Ils ne sont pas nécessaires au fonctionnement de la composition. Si nous ne définissons aucun destructeur particulier pour Voiture, le destructeur généré automatiquement par le compilateur se chargera néanmoins de détruire correctement ses objets membres.
Accueil2026 – Partie II C++ Composition et relations entre classe • Les objets membres sont détruits automatiquement lorsque l’objet qui les contient est • Le corps du destructeur de l’objet contenant est exécuté avant la destruction de ses • Les objets membres sont ensuite détruits dans l’ordre inverse de leur construction. • Si les membres sont déclarés dans l’ordre : ils sont construits dans l’ordre : Moteur −→ Batterie et détruits dans l’ordre : Batterie −→ Moteur. • Il n’est pas nécessaire d’appeler explicitement les destructeurs des objets membres : C++ s’en charge automatiquement. Composition ou héritage ? L’héritage et la composition permettent tous deux de construire de nouvelles classes à partir de classes existantes. Ils représentent cependant deux relations fondamentalement différentes. Le choix entre ces deux mécanismes ne doit donc pas être déterminé par la seule possibilité technique de les utiliser, mais par la relation réelle entre les objets que nous voulons représenter. La relation « est un » L’héritage convient lorsqu’un objet constitue une forme particulière d’un autre objet. Nous avons par exemple défini : class Carre : public Forme Cette relation possède une signification naturelle : Un carré est une forme. Un objet Carre peut donc être utilisé lorsqu’un objet Forme est attendu. Cette propriété nous a notamment permis d’utiliser le polymorphisme. La relation « possède un » La composition convient lorsqu’un objet utilise un autre objet comme l’un des éléments qui le constituent. Dans notre exemple :
Accueil2026 – Partie II C++ Composition et relations entre classe Une voiture possède un moteur. Une voiture possède une batterie. Une voiture n’est cependant ni un moteur ni une batterie. Il s’agit donc d’une relation de composition et non d’une relation d’héritage. POSSÈDE UN −→ composition Un héritage techniquement possible mais conceptuellement faux Le compilateur ne connaît pas la signification réelle des classes que nous créons. Nous pourrions donc techniquement écrire : class Voiture : public Moteur Cette déclaration indique cependant : Une voiture est un moteur. Cette relation est fausse. Le fait qu’une voiture utilise un moteur ne justifie donc absolument pas un La relation correcte est : Un héritage techniquement possible mais conceptuellement faux Le compilateur ne connaît pas la signification réelle des classes que nous créons. Nous pourrions donc techniquement écrire : class Voiture : public Moteur
Accueil2026 – Partie II C++ Composition et relations entre classe Cette déclaration indique cependant : Une voiture est un moteur. Cette relation est fausse. Le fait qu’une voiture utilise un moteur ne justifie donc absolument pas un La relation correcte est : Combiner héritage et composition L’héritage et la composition ne sont pas des mécanismes concurrents et ils peuvent parfaitement être utilisés simultanément lorsque les relations entre les objets le justifient. Nous pourrions par exemple définir plusieurs types de moteurs : class MoteurThermique : public Moteur class MoteurElectrique : public Moteur un moteur thermique est un moteur, un moteur électrique est un moteur. Il s’agit donc d’héritage. Une voiture peut ensuite être composée d’un moteur : class VoitureElectrique MoteurElectrique moteur; Nous avons maintenant simultanément :
Accueil2026 – Partie II C++ Composition et relations entre classe Les deux relations décrivent des aspects différents de l’organisation des classes. Construire des objets complexes par assemblage La composition permet de construire progressivement des objets complexes à partir d’objets plus simples et spécialisés. Une voiture pourrait par exemple être représentée par : SystemeFreinage freins; Climatisation climatisation; OrdinateurDeBord ordinateur; Chacune de ces classes peut elle-même contenir d’autres objets. Nous pouvons ainsi construire un système complexe sous la forme d’un assemblage de composants dont chacun possède une responsabilité • L’héritage et la composition représentent deux relations différentes entre les classes. • L’héritage correspond généralement à une relation « est un ». • La composition correspond généralement à une relation « possède un ». • Un objet d’une classe dérivée peut être considéré comme un objet de sa classe de base. La composition ne crée pas cette relation de substitution. • Le fait qu’un héritage soit accepté par le compilateur ne signifie pas qu’il représente correctement la relation entre les objets. • Héritage et composition peuvent être combinés dans une même architecture. • La composition permet de construire des objets complexes par assemblage d’objets plus simples possédant chacun une responsabilité précise. Plusieurs objets membres et délégation des tâches Notre classe Voiture contient maintenant deux objets membres :
Accueil2026 – Partie II C++ Composition et relations entre classe double getPuissance() const; double getCapaciteBatterie() const; : moteur(puissance), double Voiture::getPuissance() const return moteur.getPuissance(); double Voiture::getCapaciteBatterie() const return batterie.getCapacite(); La voiture ne stocke pas directement la puissance du moteur ni la capacité de la batterie. Ces informations appartiennent respectivement aux objets Moteur et Batterie. de Voiture ne connaît pas directement la représentation interne du moteur. Elle demande simplement à l’objet moteur de fournir son information : return moteur.getPuissance(); De même, la capacité de la batterie est obtenue par : return batterie.getCapacite(); La classe Voiture coordonne donc plusieurs objets spécialisés sans avoir à connaître leur représentation interne. Déléguer une responsabilité Lorsqu’une méthode d’une classe utilise un objet membre pour effectuer une opération qui relève naturellement de cet objet, on parle souvent de délégation. Dans notre exemple : Voiture −→ Moteur pour obtenir la puissance, et
Accueil2026 – Partie II C++ Composition et relations entre classe Voiture −→ Batterie pour obtenir la capacité. Chaque classe conserve ainsi une responsabilité bien définie. #include « Voiture.h » Voiture voiture(150.0, 75.0); << voiture.getPuissance() << « Capacite batterie : » << voiture.getCapaciteBatterie() Capacite batterie : 75 Program ended with exit code: 0 • Une classe peut contenir plusieurs objets membres appartenant à des classes différentes. • Chaque objet membre conserve sa propre responsabilité et sa propre encapsulation. • La classe contenant ces objets peut déléguer certaines opérations à ses composants. • La composition permet ainsi de construire des objets complexes à partir de composants spécialisés sans dupliquer leurs données ou leurs comportements. Composition et pointeurs Jusqu’à présent, notre classe Voiture contenait directement un objet de type Moteur :
Accueil2026 – Partie II C++ Composition et relations entre classe L’objet moteur fait alors réellement partie de l’objet Voiture. Sa construction et sa destruction sont automatiquement liées à celles de la voiture. Nous pouvons cependant rencontrer une organisation différente dans laquelle la classe ne contient plus directement un objet, mais un pointeur vers cet objet : Ces deux déclarations ne décrivent pas la même situation. Deux situations différentes Le membre moteur est ici un véritable objet de type Moteur. déclare seulement un pointeur capable de contenir l’adresse d’un objet de type Moteur et l’objet Moteur lui-même doit exister quelque part ailleurs. Fig. 6.1 – Composition directe Fig. 6.2 – Avec un pointeur. Un objet créé dynamiquement Supposons que le constructeur de Voiture crée lui-même le moteur par allocation dynamique : Voiture::Voiture(double puissance) moteur = new Moteur(puissance);
Accueil2026 – Partie II C++ Composition et relations entre classe new Moteur(puissance) crée dynamiquement un objet de type Moteur et retourne son adresse. Cette adresse est ensuite enregistrée dans le membre moteur. Nous pouvons également initialiser directement le pointeur dans la liste d’initialisation : Voiture::Voiture(double puissance) : moteur(new Moteur(puissance)) Le membre moteur reçoit alors directement l’adresse retournée par new. La responsabilité de la destruction Lorsque l’objet Voiture est détruit, son membre moteur est lui aussi détruit automatiquement. Mais ce membre est maintenant un pointeur donc la disparition du pointeur ne provoque pas la destruction de l’objet vers lequel il pointe. Si la voiture a créé elle-même le moteur avec new, elle doit donc également assurer sa destruction avec delete. Dans cette situation, le destructeur de Voiture n’est plus présent uniquement pour observer la destruction. Il possède une véritable responsabilité : libérer l’objet qui a été créé dynamiquement Accéder à l’objet pointé Puisque moteur est maintenant un pointeur, l’accès à ses méthodes s’effectue avec l’opérateur −>. double Voiture::getPuissance() const return moteur->getPuissance(); utilise donc l’objet Moteur dont l’adresse est contenue dans le pointeur moteur. Pointer vers un objet existant Un pointeur membre ne signifie pas nécessairement que la classe possède l’objet pointé. Un moteur pourrait avoir été créé indépendamment : Moteur moteurExterne(150.0); puis son adresse transmise à une voiture : Voiture voiture(&moteurExterne); La voiture peut alors utiliser cet objet sans l’avoir créé elle-même. Dans cette situation, la classe Voiture ne doit pas décider automatiquement de détruire l’objet pointé. Elle possède le pointeur, mais elle ne possède pas nécessairement l’objet vers lequel ce pointeur conduit. Il faut donc distinguer soigneusement :
Accueil2026 – Partie II C++ Composition et relations entre classe posséder un pointeur être responsable de l’objet pointé. Une information absente du pointeur classique indique que moteur peut contenir l’adresse d’un objet de type Moteur mais n’indique cependant pas qui est responsable de la durée de vie de celui-ci. Cette information doit être déterminée par la conception du programme. Une mauvaise interprétation de cette responsabilité peut conduire soit à une fuite mémoire, soit à la destruction incorrecte d’un objet qui ne devait pas être détruit par cette classe. Objet directement contenu Destruction par Voiture Moteur* moteur; + new Moteur* moteur; + objet externe • Un objet membre déclaré directement : est automatiquement construit et détruit avec l’objet qui le contient. • Un membre déclaré : n’est qu’un pointeur vers un objet de type Moteur. • Détruire un pointeur ne détruit pas automatiquement l’objet vers lequel il pointe. • Si une classe crée elle-même un objet avec new, elle doit assurer sa destruction avec delete dans cette approche classique. • Un pointeur classique n’indique pas qui possède l’objet pointé ni qui est responsable de • Un pointeur peut également désigner un objet appartenant à une autre partie du programme. Dans ce cas, la classe qui utilise ce pointeur ne doit pas nécessairement détruire Nous avons jusqu’à présent étudié une composition dans laquelle un objet Moteur faisait directement partie d’un objet Voiture :
Accueil2026 – Partie II C++ Composition et relations entre classe La durée de vie du moteur est alors directement liée à celle de la voiture : lorsque la voiture est détruite, son moteur est automatiquement détruit. Il existe cependant des relations plus faibles entre les objets. Une classe peut utiliser un objet qui existe indépendamment d’elle et dont elle ne contrôle pas nécessairement la durée de vie. On parle alors généralement d’agrégation. Un objet existant indépendamment Considérons un moteur créé indépendamment de la voiture : Moteur moteurExterne(150.0); Nous pouvons ensuite transmettre son adresse à un objet Voiture : Voiture voiture(&moteurExterne); La voiture peut maintenant utiliser ce moteur, mais elle ne l’a pas créé. Le moteur possède donc une existence indépendante de celle de la voiture. Le constructeur de Voiture ne construit pas ici un nouvel objet Moteur : Voiture::Voiture(Moteur* moteurExterne) : moteur(moteurExterne) Il enregistre simplement l’adresse d’un objet existant. Le pointeur permet ensuite à la voiture d’utiliser double Voiture::getPuissance() const return moteur->getPuissance(); Qui détruit l’objet ? Dans cette situation, la voiture utilise le moteur sans être responsable de sa destruction. Elle ne doit Le moteur continuera à suivre les règles correspondant à sa propre durée de vie. La destruction de la voiture ne doit pas entraîner automatiquement la destruction de l’objet qu’elle utilise. Objet contenu directement Existence indépendante non dans notre exemple Durées de vie liées
Accueil2026 – Partie II C++ Composition et relations entre classe Le pointeur ne définit pas à lui seul la relation La présence d’un pointeur ne suffit pas pour conclure qu’il s’agit d’une agrégation car une classe peut également posséder un objet créé dynamiquement et conserver son adresse dans un pointeur. Inversement, dans notre exemple d’agrégation, le pointeur désigne un objet dont l’existence est indépendante de celle de la voiture. C’est donc la relation de propriété et de durée de vie entre les objets, et non la seule syntaxe utilisée, qui permet de distinguer les situations. Utiliser une référence Une relation avec un objet externe peut également être représentée par une référence : Voiture(Moteur& moteurExterne); Le constructeur initialise alors la référence : Voiture::Voiture(Moteur& moteurExterne) : moteur(moteurExterne) La voiture utilise ainsi directement un moteur existant sans en devenir automatiquement propriétaire. • La composition représente une relation forte dans laquelle un objet fait partie de l’objet • L’agrégation représente une relation plus faible : un objet peut utiliser un autre objet dont l’existence est indépendante. • Un objet utilisé par agrégation n’est pas nécessairement détruit lorsque l’objet qui l’utilise • Un pointeur classique ne permet pas, à lui seul, de savoir qui est propriétaire de l’objet • La présence d’un pointeur ne signifie donc pas automatiquement qu’il s’agit d’une agrégation. • Une référence peut également permettre à une classe d’utiliser un objet existant. • Lorsqu’un objet conserve un pointeur ou une référence vers un objet externe, il faut s’assurer que cet objet externe existe encore lorsqu’il est utilisé.
Accueil2026 – Partie II C++ Composition et relations entre classe Comparaison des relations entre classes Nous avons maintenant rencontré plusieurs manières d’établir une relation entre des classes. Ces relations peuvent parfois conduire à des programmes dont la structure semble similaire, mais elles expriment des significations très différentes. Nous allons comparer les trois relations étudiées jusqu’ici : • la composition ; • l’agrégation. L’héritage : « est un » L’héritage établit une relation dans laquelle une classe dérivée représente une forme particulière de Nous avons par exemple utilisé : class Carre : public Forme Cette relation peut être exprimée par : Un carré est une forme. Un objet Carre peut donc être considéré comme un objet Forme. Cette propriété constitue notamment la base du polymorphisme étudié précédemment. La composition : « possède un » La composition établit une relation dans laquelle un objet contient directement un autre objet comme l’un de ses membres. Notre classe Voiture contient actuellement une batterie : Nous pouvons exprimer cette relation par : Une voiture possède une batterie. L’objet Batterie fait directement partie de l’objet Voiture. La durée de vie de cet objet membre est directement liée à celle de la voiture. Lorsque la voiture est construite, sa batterie est construite. Lorsque la voiture est détruite, sa batterie est automatiquement détruite.
Accueil2026 – Partie II C++ Composition et relations entre classe L’agrégation : utiliser un objet indépendant Notre voiture utilise également un moteur qui existe indépendamment d’elle. Elle conserve pour cela son adresse : Le pointeur moteur permet à la voiture d’accéder à un objet Moteur existant ailleurs. La voiture ne contient donc pas directement cet objet et n’est pas, dans notre modèle, responsable de sa destruction. L’objet moteurExterne possède sa propre durée de vie. La voiture peut l’utiliser, mais sa destruction n’entraîne pas la destruction de ce moteur. Nous avons donc une relation plus faible que la composition : les deux objets restent indépendants du point de vue de leur durée de vie. Plusieurs relations dans une même classe Une même classe peut naturellement entretenir des relations différentes avec d’autres classes. Notre classe Voiture en fournit maintenant un exemple : Le membre batterie représente une composition. Le membre moteur permet ici une agrégation avec un objet Moteur existant indépendamment de la voiture.
Accueil2026 – Partie II C++ Composition et relations entre classe La relation entre deux classes ne dépend donc pas uniquement du fait qu’elles apparaissent ensemble dans un programme. Elle dépend de la signification que nous donnons à cette relation, notamment en matière de substitution, de propriété et de durée de vie. Trois questions pour identifier la relation Lors de la conception de classes, quelques questions simples permettent souvent d’identifier la relation 1. L’objet peut-il réellement être considéré comme une forme particulière d’un autre objet ? EST UN −→ héritage 2. L’objet fait-il directement partie d’un autre objet, avec une durée de vie qui lui est naturellement POSSÈDE UN −→ composition 3. L’objet utilise-t-il un autre objet qui peut exister indépendamment de lui ? UTILISE UN OBJET INDÉPENDANT −→ agrégation • L’héritage exprime généralement une relation « est un ». • La composition exprime généralement une relation « possède un » dans laquelle l’objet membre fait directement partie de l’objet qui le contient. • L’agrégation représente une relation plus faible avec un objet qui peut exister indépendamment. • La composition lie naturellement la durée de vie de l’objet membre à celle de l’objet qui • Dans une agrégation, l’objet utilisé possède une durée de vie indépendante et l’objet qui l’utilise n’est pas nécessairement responsable de sa destruction. • Une même classe peut simultanément participer à plusieurs types de relations avec La programmation orientée objet ne consiste pas uniquement à créer des classes indépendantes. Dans un programme réel, les objets collaborent, se contiennent, utilisent d’autres objets ou appartiennent à des familles de types reliées par héritage. Dans ce chapitre, nous avons étudié plusieurs manières d’établir ces relations et, en particulier, la composition et l’agrégation. La composition permet de construire un objet complexe à partir d’objets plus simples. Dans notre premier modèle, une voiture contenait directement un moteur et une batterie :
Accueil2026 – Partie II C++ Composition et relations entre classe Les objets membres font alors réellement partie de l’objet Voiture. Leur durée de vie est directement liée à celle de la voiture. Cette organisation possède des conséquences précises sur la construction des objets. Les objets membres sont construits avant l’exécution du corps du constructeur de l’objet qui les Lorsqu’une liste d’initialisation est utilisée : : moteur(puissance), elle indique comment construire chacun des objets membres. L’ordre réel de construction est toutefois déterminé par l’ordre de déclaration des membres dans la classe et non par leur ordre dans la liste d’initialisation. Lors de la destruction, les objets membres sont automatiquement détruits dans l’ordre inverse de leur construction. La composition permet également de répartir les responsabilités entre plusieurs classes. Une voiture n’a pas besoin de connaître la représentation interne de son moteur ou de sa batterie. Elle peut déléguer certaines opérations aux objets qu’elle contient : double Voiture::getCapaciteBatterie() const return batterie.getCapacite(); Chaque classe peut ainsi conserver une responsabilité clairement définie. Nous avons ensuite rencontré une relation plus faible : l’agrégation. Dans notre modèle final, la voiture contient directement sa batterie mais utilise un moteur existant Nous avons ainsi simultanément : Voiture – Batterie
Accueil2026 – Partie II C++ Composition et relations entre classe L’utilisation d’un pointeur nous a également montré qu’une adresse ne contient aucune information indiquant qui est propriétaire de l’objet pointé. indique seulement où se trouve un objet de type Moteur. Il ne permet pas, à lui seul, de déterminer qui est responsable de la durée de vie et de la destruction de cet objet. Cette question de propriété deviendra particulièrement importante lorsque nous étudierons les mécanismes modernes de gestion Nous disposons maintenant de trois relations fondamentales permettant d’organiser nos classes : composition : POSSÈDE UN agrégation : UTILISE UN OBJET INDÉPENDANT Ces relations peuvent être combinées pour construire progressivement des architectures composées d’objets spécialisés qui collaborent entre eux. • Une classe peut contenir directement des objets appartenant à d’autres classes. • Dans une composition, les objets membres font partie de l’objet qui les contient et leurs durées de vie sont naturellement liées. • Les objets membres sont construits dans leur ordre de déclaration et détruits dans l’ordre • La liste d’initialisation permet de construire directement les objets membres avec les paramètres appropriés. • La composition permet à une classe de déléguer certaines responsabilités aux objets spécialisés qu’elle contient. • Dans une agrégation, un objet peut utiliser un autre objet possédant une existence • Un pointeur classique indique l’adresse d’un objet mais ne précise pas qui est propriétaire de cet objet ni qui doit assurer sa destruction. • Héritage, composition et agrégation représentent des relations différentes et peuvent être combinés dans une même architecture objet.
AccueilP2-Chap-06- Composition et relations entre classe
Accueil2026 – Partie II C++ Composition et relations entre classe
AccueilTermes à ajouter au glossaire