Initialization
Initialisation d'une variable fournit sa valeur initiale au moment de la construction.
La valeur initiale peut être fournie dans la section d'initialisation d'un déclarateur ou d'une expression new . Elle intervient également lors des appels de fonction : les paramètres de fonction et les valeurs de retour de fonction sont également initialisés.
Table des matières |
Initialiseurs
Pour chaque déclarateur, l' initializer (s'il existe) peut être l'un des suivants :
=
expression
|
(1) | ||||||||
= {}
= {
initializer-list
}
= {
designated-initializer-list
}
|
(2) |
(depuis C++20) |
|||||||
(
expression-list
)
(
initializer-list
)
|
(3) |
(jusqu'à C++11)
(depuis C++11) |
|||||||
{}
{
initializer-list
}
{
designated-initializer-list
}
|
(4) |
(depuis C++11)
(depuis C++11) (depuis C++20) |
|||||||
| expression | - | toute expression (sauf les expressions virgule non parenthésées) |
| expression-list | - | une liste d'expressions séparées par des virgules (sauf les expressions virgule non parenthésées) |
| initializer-list | - | une liste de clauses d'initialisation séparées par des virgules (voir ci-dessous) |
| designated-initializer-list | - | une liste de clauses d'initialisation désignées séparées par des virgules |
Une
clause d'initialisation
peut être l'une des suivantes :
| expression | (1) | ||||||||
{}
|
(2) | ||||||||
{
liste-d'initialisation
}
|
(3) | ||||||||
{
liste-d'initialisateurs-désignés
}
|
(4) | (depuis C++20) | |||||||
Syntaxes (2-4) sont collectivement appelées brace-enclosed initializer list .
Sémantique des initialiseurs
Si aucun initialiseur n'est spécifié pour un objet, l'objet est default-initialized . Si aucun initialiseur n'est spécifié pour une reference , le programme est mal formé.
Si l'initialiseur spécifié pour un objet est ( ) (ne peut pas apparaître dans les déclarateurs en raison de la restriction syntaxique), l'objet est value-initialized . Si l'initialiseur spécifié pour une référence est ( ) , le programme est mal formé.
La sémantique des initialiseurs est la suivante :
- Si l'entité initialisée est une référence, voir reference initialization .
-
Sinon, l'entité initialisée est un objet. Étant donné le type de l'objet comme
T:
-
- Si l'initialiseur est de syntaxe (1) , l'objet est copy-initialized .
|
(jusqu'à C++11) |
|
(depuis C++11) |
-
- Si l'initialiseur est de syntaxe (3) , l'objet est direct-initialized .
#include <string> std::string s1; // initialisation par défaut std::string s2(); // PAS une initialisation ! // déclare en fait une fonction « s2 » // sans paramètre et retournant std::string std::string s3 = "hello"; // initialisation par copie std::string s4("hello"); // initialisation directe std::string s5{'a'}; // initialisation par liste (depuis C++11) char a[3] = {'a', 'b'}; // initialisation d'agrégat // (partie de l'initialisation par liste depuis C++11) char& c = a[0]; // initialisation de référence
Variables non locales
Toutes les variables non locales avec une durée de stockage statique sont initialisées lors du démarrage du programme, avant l'exécution de la fonction main (sauf report, voir ci-dessous). Toutes les variables non locales avec une durée de stockage locale au thread sont initialisées lors du lancement du thread, séquencées avant le début de l'exécution de la fonction du thread. Pour ces deux classes de variables, l'initialisation se produit en deux étapes distinctes :
Initialisation statique
Il existe deux formes d'initialisation statique :
En pratique :
- L'initialisation constante est généralement appliquée au moment de la compilation. Les représentations d'objets pré-calculées sont stockées dans le cadre de l'image du programme. Si le compilateur ne fait pas cela, il doit quand même garantir que l'initialisation se produit avant toute initialisation dynamique.
-
Les variables à initialiser à zéro sont placées dans le segment
.bssde l'image du programme, qui n'occupe aucun espace sur le disque et est mis à zéro par le système d'exploitation lors du chargement du programme.
Initialisation dynamique
Une fois que toutes les initialisations statiques sont terminées, l'initialisation dynamique des variables non locales se produit dans les situations suivantes :
|
2)
Initialisation dynamique partiellement ordonnée
, qui s'applique à toutes les variables inline qui ne sont pas une spécialisation implicitement ou explicitement instanciée. Si un V partiellement ordonné est défini avant un W ordonné ou partiellement ordonné dans chaque unité de traduction, l'initialisation de V est séquencée avant l'initialisation de W (ou se produit-avant, si le programme démarre un thread).
|
(depuis C++17) |
Si l'initialisation d'une variable non locale avec une durée de stockage statique ou thread se termine via une exception, std::terminate est appelée.
Initialisation dynamique précoce
Les compilateurs sont autorisés à initialiser les variables à initialisation dynamique dans le cadre de l'initialisation statique (essentiellement, au moment de la compilation), si les deux conditions suivantes sont remplies :
En raison de la règle ci-dessus, si l'initialisation d'un objet
o1
fait référence à un objet de portée de namespace
o2
, qui nécessite potentiellement une initialisation dynamique, mais est défini ultérieurement dans la même unité de traduction, il n'est pas spécifié si la valeur de
o2
utilisée sera la valeur de
o2
entièrement initialisé (car le compilateur a promu l'initialisation de
o2
au moment de la compilation) ou sera la valeur de
o2
simplement initialisé à zéro.
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // non spécifié : // initialisé dynamiquement à 0.0 si d1 est initialisé dynamiquement, ou // initialisé dynamiquement à 1.0 si d1 est initialisé statiquement, ou // initialisé statiquement à 0.0 (car ce serait sa valeur // si les deux variables étaient initialisées dynamiquement) double d1 = fd(); // peut être initialisé statiquement ou dynamiquement à 1.0
Initialisation dynamique différée
Il est défini par l'implémentation si l'initialisation dynamique se produit avant la première instruction de la fonction main (pour les statiques) ou la fonction initiale du thread (pour les thread-locals), ou si elle est différée pour se produire après.
Si l'initialisation d'une variable non inline (depuis C++17) est différée pour se produire après la première instruction de la fonction main/thread, elle se produit avant la première ODR-use de toute variable avec une durée de stockage statique/thread définie dans la même unité de traduction que la variable à initialiser. Si aucune variable ou fonction n'est ODR-usée depuis une unité de traduction donnée, les variables non locales définies dans cette unité de traduction peuvent ne jamais être initialisées (cela modélise le comportement d'une bibliothèque dynamique à la demande). Cependant, tant que quelque chose d'une unité de traduction est ODR-usé, toutes les variables non locales dont l'initialisation ou la destruction a des effets secondaires seront initialisées même si elles ne sont pas utilisées dans le programme.
|
Si l'initialisation d'une variable inline est différée, elle se produit avant la première ODR-use de cette variable spécifique. |
(since C++17) |
// ============ // == Fichier 1 == #include "a.h" #include "b.h" B b; A::A() { b.Use(); } // ============ // == Fichier 2 == #include "a.h" A a; // ============ // == Fichier 3 == #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); } // Si a est initialisé avant l'entrée dans main, b peut encore être non initialisé // au moment où A::A() l'utilise (car l'initialisation dynamique est // séquencée de manière indéterminée entre les unités de traduction) // Si a est initialisé à un moment après la première instruction de main (qui utilise odr // une fonction définie dans le Fichier 1, forçant son initialisation dynamique à s'exécuter), // alors b sera initialisé avant son utilisation dans A::A
Variables locales statiques
Pour l'initialisation des variables statiques locales (c'est-à-dire de portée de bloc) et des variables thread-local, voir static block variables .
L'initialiseur n'est pas autorisé dans une déclaration de variable à portée de bloc avec liaison externe ou interne . Une telle déclaration doit apparaître avec extern et ne peut pas être une définition.
Membres de classe
Les membres de données non statiques peuvent être initialisés avec une liste d'initialisation de membre ou avec un initialiseur de membre par défaut .
Notes
L'ordre de destruction des variables non locales est décrit dans std::exit .
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement tel que publié | Comportement correct |
|---|---|---|---|
| CWG 270 | C++98 |
l'ordre d'initialisation des membres de données statiques
des modèles de classe n'était pas spécifié |
spécifié comme non ordonné sauf pour
les spécialisations et définitions explicites |
| CWG 441 | C++98 |
les références non locales avec durée de stockage statique n'étaient
pas toujours initialisées avant les initialisations dynamiques |
considérées comme une initialisation statique, toujours
initialisées avant les initialisations dynamiques |
| CWG 1415 | C++98 |
une déclaration de variable
extern
de portée de bloc
pouvait être une définition |
interdite (aucun initialiseur
autorisé dans de telles déclarations) |
| CWG 2599 | C++98 |
il n'était pas clair si l'évaluation des arguments de fonction
dans l'initialiseur fait partie de l'initialisation |
cela fait partie de l'initialisation |
Voir aussi
- copie élision
- constructeur de conversion
- constructeur de copie
- constructeur par défaut
-
explicit - constructeur de déplacement
-
new
|
Documentation C
pour
Initialization
|