8 votes

Comment passer des paramètres à un constructeur?

J'ai une classe A, qui est constituée d'objets B et C. Comment écrire un constructeur de A qui reçoit des objets B et C? Dois-je les passer par valeur, par référence (constante) ou par pointeur? Où dois-je les désallouer?

J'ai pensé aux pointeurs, car alors je pourrais écrire:

A a(new B(1,2,3,4,5), new C('x','y','z'))

Mais je ne sais pas si c'est une bonne pratique ou non. Des suggestions?

10voto

Philipp Points 21479

Généralement, vous passez par référence constante :

A a(B(1,2,3,4,5), C('x','y','z'))

Pas besoin de pointeurs ici.

Généralement, vous stockez des valeurs sauf si la copie est trop inefficace. La définition de la classe est alors la suivante:

class A {
private:
    B b;
    C c;
public:
    A(const B& b, const C& c): b(b), c(c) { }
};

4voto

SigTerm Points 16055

Dois-je les passer par valeur, par référence (constante) ou par pointeur?

  1. Par référence constante si l'objet est grand
  2. par valeur si l'objet est petit
  3. par pointeur constant s'il s'agit d'un argument facultatif pouvant être nul (c'est-à-dire "NULL")
  4. par pointeur s'il s'agit d'un argument facultatif pouvant être nul mais qui sera possédé (c'est-à-dire désalloué) par la classe construite.

Veuillez noter que si votre classe a des instances internes de B et C, les passer par référence, valeur ou référence constante impliquera très probablement l'utilisation du constructeur de copie ou de l'opérateur d'affectation. Ce qui ne sera pas nécessaire avec les pointeurs.

A a(new B(1,2,3,4,5), new C('x','y','z'))

Normalement (c'est-à-dire pas toujours), c'est une mauvaise idée, car:

  1. si A ne désalloue pas les arguments, vous avez une fuite mémoire.
  2. Si A prend possession des arguments et les désalloue, alors vous ne pourrez pas passer des valeurs allouées sur la pile en tant qu'arguments. Néanmoins, en fonction de la conception de votre code, cela peut être acceptable (Qt 4 prend fréquemment possession des objets créés avec new)

Où devrais-je les désallouer?

La meilleure idée est de vous assurer que le compilateur désalloue automatiquement les arguments pour vous. Cela signifie passer par référence, référence constante ou par valeur. Ou utiliser des pointeurs intelligents.

2voto

Peter Alexander Points 31990

Ce que vous passez dépend de vos besoins.

Avez-vous besoin d'une copie de la chose que vous passez ? Alors passez par const-référence.

struct A
{
    A(const B& b, const C& c) : m_b(b), m_c(c) {}

private:
    B m_b;
    C m_c;
};

Et construisez-le comme ceci :

A myA(B(1,2,3), C(4,5,6));

Si vous voulez que votre objet A fasse référence à d'autres objets B et C (mais ne les possède pas) alors utilisez des pointeurs (ou éventuellement des références).

1voto

utnapistim Points 12060

É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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X