Éditer : Les exemples donnés ici ne respectent pas la règle des Trois Grands (merci @Philipp !). Si la définition de A est utilisée telle que donnée ci-dessous, le code plantera lors de la construction par copie de A, ou lors de l'assignation pour A. Pour définir correctement le code, l'opérateur d'assignation et le constructeur de copie devraient être explicitement définis pour A (ou explicitement interdits - déclarés comme privés et jamais implémentés). (fin de l'édition)
Dois-je les passer par valeur, par (const) référence, ou par pointeur ?
Si A utilise B et C, alors gardez-les par référence ou par pointeur à l'intérieur de A. Pour choisir entre référence et pointeur, vérifiez comment B et C sont alloués.
S'ils sont des objets de pile locaux construits dans le même contexte que A, alors passez-les par référence constante.
S'ils sont des objets alloués dynamiquement que A utilise, faites en sorte que A les possède : passez-les par pointeurs, et faites que le destructeur de A les supprime.
S'ils sont des composants optionnels de A, passez-les par pointeur (qui peuvent être nuls).
Si A n'est pas responsable de les supprimer, passez-les par * const
.
Où devrais-je les désallouer ?
Généralement là où vous n'en avez plus besoin :).
S'ils sont nécessaires au-delà du contexte de A (s'ils sont des objets externes que A utilise) alors supprimez-les lorsque le contexte de A est complet.
S'ils sont possédés par A, supprimez-les dans le destructeur de A. Il peut être pertinent de les supprimer également pendant la durée de vie de A, si les pointeurs doivent être modifiés.
Voici un exemple, où B est un composant remplaçable injecté dans A (et possédé par A) et C est un composant optionnel possédé par A (mais injecté dans A également).
("possédé par" signifie que A est responsable de supprimer les deux objets)
class B;
class C;
class A
{
B* b;
C* c;
public:
A(B* const bb, C* const cc = 0) // cc est optionnel
: b(bb), c(cc)
{
}
void resetB(B* const bb = 0)
{
delete b;
b = bb;
}
~A()
{
resetB();
delete c;
}
};
{
A a(new B, new C);
a.resetB(); // supprime B
a.resetB(new B); // supprime l'ancien B et en définit un nouveau
} // les deux membres de A sont supprimés
Mais je ne sais pas si c'est une bonne pratique ou non. Des suggestions ?
C'est vraiment à vous de décider, mais vous pouvez écrire A a(B(1, 2, 4), C(1, 2, 3))
aussi facilement que A a(new B(1, 2, 4), new C(1,2,3));
(dans le premier cas - celui sans new - les A::b et A::c devraient être des références ou des objets/valeurs à l'intérieur de la classe, et A ne devrait pas les supprimer du tout).
La question ne devrait pas être de savoir si vous voulez écrire l'instruction avec une allocation dynamique pour B et C mais si vous en avez besoin. L'allocation dynamique est lente et si vous n'en avez pas besoin, vous ne devriez pas le faire.