La liste d’initialisation sera-t-elle toujours traitée avant le code constructeur?

La liste d’initialisation sera-t-elle toujours traitée avant le code constructeur?

En d’autres termes, le code suivant affichera-t-il toujours et la classe construite aura-t-elle la valeur “connue” pour source_ (si la variable globale est something true )?

 class Foo { std::ssortingng source_; public: Foo() : source_("") { std::cout << source_ << std::endl; if(something){ source_ = "known"; } } }; 

Oui, selon C++11: 12.6.2 /10 (même section en C++14 , 15.6.2 /13 en C++17 ):


Dans un constructeur non délégant, l’initialisation se déroule dans l’ordre suivant (mon gras):

  • Tout d’abord, et uniquement pour le constructeur de la classe la plus dérivée (1.8), les classes de base virtuelles sont initialisées dans l’ordre dans lequel elles apparaissent sur le parcours profondeur-première gauche à droite du graphe acyclique dirigé des classes de base, où «left- to-right ”est l’ordre d’apparition des classes de base dans la classe dérivée base-specifier-list.

  • Ensuite, les classes de base directes sont initialisées dans l’ordre de déclaration telles qu’elles apparaissent dans la liste de spécificateurs de base (quel que soit l’ordre des initialiseurs de mémoire).

  • Ensuite, les membres de données non statiques sont initialisés dans l’ordre dans lequel ils ont été déclarés dans la définition de classe (là encore quel que soit l’ordre des initialiseurs de mémoire).

  • Enfin, l’instruction composée du corps du constructeur est exécutée.


La raison principale d’utiliser init-lists est d’aider le compilateur à l’optimisation. Les listes d’initiatives pour les types non basiques (c’est-à-dire les objects de classe plutôt que int , float , etc.) peuvent généralement être construites sur place.

Si vous créez l’object, puis l’assignez dans le constructeur, cela se traduit généralement par la création et la destruction d’objects temporaires, ce qui est inefficace.

Les listes d’initialisation peuvent éviter cela (si le compilateur en a la capacité, bien sûr, mais la plupart d’entre elles devraient l’être).

Le programme complet suivant produira le résultat 7, mais il s’agit d’un compilateur spécifique (CygWin g ++). Il ne garantit donc pas ce comportement plus que l’exemple de la question initiale.

Cependant, comme indiqué dans le premier paragraphe ci-dessus, la norme le garantit.

 #include  class Foo { int x; public: Foo(): x(7) { std::cout << x << std::endl; } }; int main (void) { Foo foo; return 0; } 

Oui, C ++ construit tous les membres avant d’appeler le code de construction.

Comme déjà répondu, les listes d’initialisation sont complètement exécutées avant d’ entrer dans le bloc constructeur. Il est donc parfaitement sûr d’utiliser des membres (initialisés) dans le corps du constructeur.

Vous avez fait un commentaire dans la réponse acceptée sur la nécessité de faire référence aux arguments du constructeur, mais pas aux vars de membre du bloc constructeur. Vous pas.

Il est possible que vous ayez mal interprété le fait que vous deviez faire référence à des parameters et non à des atsortingbuts de membre dans la liste d’initialisation. Par exemple, étant donné une classe X ayant deux membres (a_ et b_) de type int, le constructeur suivant peut être mal défini:

  X::X( int a ) : a_( a ), b( a_*2 ) {} 

Le problème possible ici est que la construction des éléments de la liste d’initialisation dépend de l’ordre de déclaration dans la classe et non de l’ordre dans lequel vous tapez la liste d’initialisation. Si la classe était définie comme:

 class X { public: X( int a ); private: int b_; int a_; }; 

Ensuite, quelle que soit la manière dont vous tapez la liste d’initialisation, le fait est que b_ (a_ * 2) sera exécuté avant l’ initialisation de a_ car la déclaration des membres est d’abord b_ puis a_. Cela créera un bogue car votre code croit (et dépend probablement) du fait que b_ représente le double de la valeur de a_, et en fait, b_ contient des déchets. La solution la plus simple ne fait pas référence aux membres:

  X::X( int a ) : a_( a ), b( a*2 ) {} // correct regardless of how X is declared 

Pour éviter ce piège, il vous est suggéré de ne pas utiliser les atsortingbuts de membre lors de l’initialisation d’autres membres.