A quoi sert la liste des variables membres après les deux points dans un constructeur?

StackOverflow https://stackoverflow.com/questions/210616

  •  03-07-2019
  •  | 
  •  

Question

Je lis ce code source ouvert C ++ et je suis arrivé chez un constructeur mais je ne le comprends pas (essentiellement parce que je ne connais pas C ++: P)

Je comprends très bien C et Java.

 TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
     _someMethod( 0 ),
     _someOtherMethod( 0 ),
     _someOtherOtherMethod( 0 ),
     _someMethodX( 0 ) 
  {
       int bla;
       int bla;
  }

Pour autant que je puisse "en déduire" La première ligne ne déclare que le nom du constructeur, le "& ::;" " ressemble à " appartient à " pour moi. Et le code entre {} est le constructeur du corps lui-même.

Je "pense" qu’est-ce qui suit les paramètres et le premier & {; \ " sont comme les méthodes des paramètres par défaut ou quelque chose, mais je ne trouve pas une explication raisonnable sur le web. La plupart des constructeurs C ++ que j'ai trouvés dans les exemples sont presque identiques à ceux de Java.

Est-ce que j'ai raison dans mes hypothèses? " :: " est comme appartient à, et la liste après params et body sont comme "default default" ou quelque chose?

MISE À JOUR: Merci pour les réponses. Peut-on appeler ces méthodes? (Je suppose que non) et quelle est la différence d'appeler dans le corps du constructeur

Était-ce utile?

La solution

Le cas le plus courant est le suivant:

class foo{
private:
    int x;
    int y;
public:
    foo(int _x, int _y) : x(_x), y(_y) {}
}

Ceci définira x et y sur les valeurs indiquées dans _x et _y dans le constructeur. paramètres. C’est souvent le meilleur moyen de construire des objets déclarés membres de données.

Il est également possible que vous examiniez l'enchaînement des constructeurs:

class foo : public bar{
    foo(int x, int y) : bar(x, y) {}
};

Dans ce cas, le constructeur de la classe appelle le constructeur de sa classe de base et transmet les valeurs x et y .

Pour disséquer la fonction encore plus loin:

TransparentObject::TransparentObject( int w, int x, int y, int z ) : 
   _someMethod( 0 ),
   _someOtherMethod( 0 ),
   _someOtherOtherMethod( 0 ),
   _someMethodX( 0 ) 
{
     int bla;
     int bla;
}

L'opérateur :: est appelé opérateur de résolution de portée. En gros, cela indique simplement que TransparentObject est membre de TransparentObject . Deuxièmement, vous avez raison de supposer que le corps du constructeur apparaît entre des accolades.

  

UPDATE: Merci pour les réponses. Peut-on appeler ces méthodes? (Je suppose que non) et quelle est la différence d'appeler dans le corps du constructeur

Il existe beaucoup plus d'informations à ce sujet que je ne pourrais vous donner ici . La zone la plus courante dans laquelle vous devez utiliser des listes d'initialisation est celle qui précède l'initialisation d'une référence ou d'un const , car ces variables doivent recevoir une valeur immédiatement après leur création.

Autres conseils

Vous êtes assez proche. La première ligne est la déclaration. L'étiquette à gauche de :: est le nom de la classe et pour qu'il s'agisse d'un constructeur, le nom de la fonction doit être identique à celui de la classe.

TransparentObject::TransparentObject( int w, int x, int y, int z )

En C ++, vous pouvez éventuellement placer deux points et quelques valeurs initiales pour les variables membres avant le début du corps de la fonction. Cette technique doit être utilisée si vous initialisez une variable const ou si vous transmettez des paramètres à un constructeur de superclasse.

: 
 _someMethod( 0 ),
 _someOtherMethod( 0 ),
 _someOtherOtherMethod( 0 ),
 _someMethodX( 0 )

Vient ensuite le corps du constructeur entre accolades.

{
   int bla;
   int bla;
}

:: En réalité signifie contient (voir les commentaires pour clarification), cependant, _someMethods et ainsi de suite est ce qu'on appelle un liste d'initialisation . Il y a beaucoup d'informations sur le lien =]

EDIT: Désolé, ma première phrase est incorrecte - voir les commentaires.

Oui, :: est l'opérateur de cadrage C ++ qui vous permet d'indiquer au compilateur à quoi la fonction appartient. Avec a: après la déclaration du constructeur, on appelle ce que l’on appelle une liste d’initialisation.

Le code entre la liste d'arguments et le {} s spécifie l'initialisation de (certains des) membres de la classe.

L’initialisation par opposition à l’affectation --- ce sont des choses différentes --- donc ce sont tous des appels aux constructeurs.

Vous avez raison. C'est un moyen de définir les valeurs par défaut pour les variables de classe. Je ne connais pas trop la différence entre les placer après: et dans le corps de la fonction.

Il existe généralement de bonnes raisons d'utiliser une liste d'initialisation. D'une part, vous ne pouvez pas définir de variables de membre qui sont des références en dehors de la liste d'initialisation du constructeur. De plus, si une variable membre a besoin de certains arguments pour son propre constructeur, vous devez les transmettre ici. Comparez ceci:

class A
{
public:
  A();
private:
  B _b;
  C& _c;
};

A::A( C& someC )
{
  _c = someC; // this is illegal and won't compile. _c has to be initialized before we get inside the braces
  _b = B(NULL, 5, "hello"); // this is not illegal, but B might not have a default constructor or could have a very 
                            // expensive construction that shouldn't be done more than once
}

vers cette version:

A::A( C& someC )
: _b(NULL, 5, "hello") // ok, initializing _b by passing these arguments to its constructor
, _c( someC ) // this reference to some instance of C is correctly initialized now
{}

Sans utiliser la liste d'initialisation, tous les membres de la classe auront simplement leur constructeur par défaut appelé; c'est donc le seul endroit où vous pouvez contrôler le constructeur qui est appelé (pour les membres alloués de manière non dynamique). Il en va de même pour le constructeur de la classe parent qui sera appelé.

Membres de classe " initialisés " dans le corps du constructeur (c'est-à-dire entre les accolades {} à l'aide de l'opérateur =) n'est pas une initialisation technique, c'est une affectation. Pour les classes avec un constructeur / destructeur non trivial, il peut être coûteux de construire par défaut, puis de le modifier par affectation de cette manière. Pour les membres de référence, vous devez utiliser la liste des initialiseurs, car ils ne peuvent pas être modifiés via l'opérateur d'affectation.

Si le membre (ou la classe parente) n'a pas de constructeur par défaut, le fait de ne pas spécifier un constructeur approprié dans la liste des initialiseurs provoquera une erreur du compilateur. Sinon, le compilateur insérera le constructeur par défaut lui-même. Pour les types prédéfinis, cela ne fait rien et vous y trouverez des valeurs parasites.

Notez que l'ordre dans lequel vous spécifiez les membres dans la liste des initialiseurs n'affecte pas l'ordre dans lequel ils sont appelés . C'est toujours le constructeur de la classe parente (le cas échéant) en premier, puis les membres de la classe dans l'ordre dans lequel ils ont été définis dans la définition de la classe. L'ordre dans lequel vous les mettez dans la liste des initialiseurs n'a pas d'importance et peut être la source de bugs subtils ...

Dans l'exemple ci-dessous, il semble que l'intention soit d'initialiser m_b avec valeur puis m_a avec m_b , mais ce qui se passe réellement, c'est que m_a est initialisé avec m_b (lui-même non encore initialisé), puis m_b est initialisé avec valeur . m_b ne contiendra que des déchets!

struct BadInitialiserListExample
{
    BadInitialiserListExample(int value) :
        m_b(value),
        m_a(m_b)      // <-- *BUG* this is actually executed first due to ordering below!
    {
    }

    int    m_a;
    int    m_b;
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top