Il est parfaitement permis à une classe davoir un ou plusieurs membres instances dautres classes. En utilisant toujours les classes dexemples du début, voici une classe contenant des membres instances :
class multiple { exemple ex; autre au; public : multiple() {} // constructeur par défaut // ... };
Tout le problème consiste à initialiser ces membres dans le constructeur de la classe. La solution immédiate :
multiple::multiple(){ ex.exemple::exemple(); // incorrect au.autre::autre(0); // idem}
est incorrecte, car on ne peut pas appeler explicitement un constructeur.
Le langage fournit un mécanisme spécial pour résoudre ce problème. Derrière la liste des arguments du constructeur, on place le symbole deux-points :
, suivi des initialisations de chaque membre, séparées par une virgule :
multiple::multiple() : ex(), au(0); // constructeur par défaut
Les constructeurs sont appelés dans lordre de leur écriture (donc ici dabord exemple::exemple()
, puis autre::autre(0)
).
Cette séquence dinitialisation doit être écrite dans chaque constructeur, sil y a lieu :
class multiple { exemple ex; autre au; public : multiple() : ex(), au(0) {} multiple(double d) : ex(), au(d) {} multiple(double d, int i, char c) : ex(i, c), au(d) {} multiple(autre& a, exemple& e) : ex(e), au(a) {} multiple(multiple& m) : ex(m.ex), au(m.au) {} // ... };
Le dernier constructeur est un constructeur de copie ; comme celui qui le précède, il appelle les constructeurs de copie des classes exemple
et autre
(qui rappelons-le sont toujours définis).
Tous les types ont un constructeur de copie, pas seulement les classes. De ce fait, même une classe très ordinaire peut initialiser ses membres de cette façon :
class ordinaire { double dd; int ii; public : ordinaire(int i, double d) : ii(i), dd(d) {} // ... };
Ce constructeur est équivalent à :
ordinaire(int i, double d) { ii = i; dd = d;}
Lintérêt de la première notation est assez faible dans ce cas, la seconde est bien plus claire.
Lorsque le destructeur dune classe est appelé, automatiquement ou non, les destructeurs des membres sont exécutés à la fin de celui-ci de manière automatique.
Lorsquune classe contient des références à dautres types (classes ou non), le mécanisme dinitialisation est strictement identique. Cependant, dans ce cas, linitialisation est obligatoire. Ainsi lécriture :
class fautive { int& i; public: fautive() {}; // NON, pas dinit. de la référence };
provoque une erreur Error : Reference member 'i' is not initialized in function fautive::fautive()
, le membre référence 'i' nest pas initialisé.
Il faut dans ce cas écrire obligatoirement une initialisation utilisant le constructeur de copie, et aucun autre. Par exemple lécriture suivante pour le constructeur de fautive
:
fautive() : i(22) {}
est refusée par le compilateur (Error : Reference member 'i' need a temporary for initialization, le membre référence 'i' nécessite une variable temporaire pour linitialisation).
Cest tout à fait logique : le compilateur ne peut pas savoir si un membre référence a été initialisé sur une variable temporaire au moment de la destruction. En conséquence, il interdit dinitialiser une référence sur une variable temporaire, et en contrepartie il ny a jamais dappel de destructeur sur les membres références au moment de la destruction de linstance. Si ce mécanisme ne vous convient pas, il faut utiliser un membre normal, non une référence.
Les seuls constructeurs acceptables pour que la classe fautive
ne le soit plus seraient :
fautive::fautive() : i(j) { }fautive::fautive(int k) : i(k) { };
où j
est une variable globale, ou un autre membre de la classe fautive
.
Une classe peut aussi contenir des pointeurs sur des instances dautres classes. Dans ce cas, il ny a aucune difficulté particulière :
class multiple { exemple *pex; autre *pau; public : multiple() { pex = new exemple; pau = new autre(0); } multiple(autre& a, exemple& e) { pex = new exemple(e); pau = new autre(a);} multiple(multiple& m) { pex = new exemple(*m.pex); pau = new autre (*m.pau); } ~multiple() { delete pau; delete pex; } // ... };
Le destructeur doit alors explicitement libérer la mémoire occupée par les instances pointées.
Précédent | Sommaire | Suivant |