Constructors and member initializer lists
Constructeurs sont des fonctions membres non statiques déclarées avec une syntaxe de déclarateur spéciale, ils sont utilisés pour initialiser des objets de leurs types de classe.
|
Un constructeur ne peut pas être une coroutine . |
(since C++20) |
|
Un constructeur ne peut pas avoir de paramètre objet explicite . |
(since C++23) |
Table des matières |
Syntaxe
Les constructeurs sont déclarés en utilisant des déclarateurs de fonction membre de la forme suivante :
class-name
(
parameter-list
(optionnel)
)
except
(optionnel)
attr
(optionnel)
|
|||||||||
| class-name | - | une expression d'identifiant , éventuellement suivie d'une liste d'attributs , et (depuis C++11) éventuellement entourée d'une paire de parenthèses | ||||||
| parameter-list | - | liste de paramètres | ||||||
| except | - |
|
||||||
| attr | - | (depuis C++11) une liste d'attributs |
Les seuls spécificateurs autorisés dans les
spécificateurs de déclaration
d'une déclaration de constructeur sont
friend
,
inline
,
constexpr
(depuis C++11)
,
consteval
(depuis C++20)
, et
explicit
(en particulier, aucun type de retour n'est autorisé). Notez que les
qualificateurs cv et ref
ne sont pas non plus autorisés : la sémantique const et volatile d'un objet en cours de construction ne s'applique qu'après la fin du constructeur de la classe la plus dérivée.
L'expression d'identifiant du class-name doit avoir l'une des formes suivantes :
- Dans une déclaration friend , l'expression identifiante est un identifiant qualifié qui nomme un constructeur .
- Sinon, dans une déclaration de membre qui appartient à la spécification de membre d'une classe ou d'un modèle de classe :
-
- Pour les classes, l'expression identifiante est le injected-class-name de la classe immédiatement englobante.
- Pour les modèles de classe, l'expression identifiante est un nom de classe qui désigne l' current instantiation (jusqu'en C++20) injected-class-name (depuis C++20) du modèle de classe immédiatement englobant.
- Sinon, l'expression d'identifiant est un identifiant qualifié dont l'identifiant non qualifié terminal est le nom de classe injecté de son contexte de recherche .
Liste d'initialisation des membres
Le corps d'une
définition de fonction
de tout constructeur de la classe
T
, avant l'accolade ouvrante de l'instruction composée, peut inclure la
liste d'initialisation des membres
, dont la syntaxe est le caractère deux-points
:
, suivi de la liste séparée par des virgules d'un ou plusieurs
initialiseurs-de-membre
s, chacun ayant la syntaxe suivante :
| initialiseur de membre | (1) | ||||||||
| initialiseur de classe | (2) | ||||||||
initialiseur de pack de classe
...
|
(3) | (depuis C++11) | |||||||
|
(depuis C++11) |
-
Une classe de base directe ou une
classe de base virtuelle
de
T. Dans ce cas, le sous-objet de classe de base correspondant est initialisé directement avec initializer .
| member | - | un identifiant qui nomme un membre de données |
| class | - | un nom de classe |
| class-pack | - | un pack qui s'étend à zéro ou plusieurs classes |
| initializer | - |
un
initializer
qui ne commence pas par
=
|
struct S { int n; S(int); // déclaration du constructeur S() : n(7) {} // définition du constructeur : // « : n(7) » est la liste d'initialisation // « : n(7) {} » est le corps de la fonction }; S::S(int x) : n{x} {} // définition du constructeur : « : n{x} » est la liste d'initialisation int main() { S s; // appelle S::S() S s2(10); // appelle S::S(int) }
Explication
Les constructeurs n'ont pas de noms et ne peuvent pas être appelés directement. Ils sont invoqués lors de l'initialisation , et ils sont sélectionnés selon les règles d'initialisation. Les constructeurs sans explicit sont des constructeurs de conversion . Les constructeurs avec un spécificateur constexpr rendent leur type littéral . Les constructeurs qui peuvent être appelés sans argument sont des constructeurs par défaut . Les constructeurs qui prennent un autre objet du même type comme argument sont des constructeurs de copie et des constructeurs de déplacement .
Avant que l'instruction composée formant le corps de la fonction du constructeur ne commence son exécution, l'initialisation de toutes les bases directes, bases virtuelles et membres de données non statiques est terminée. La liste d'initialisation des membres est l'endroit où l'initialisation non par défaut de ces sous-objets peut être spécifiée. Pour les bases qui ne peuvent pas être initialisées par défaut et pour les membres de données non statiques qui ne peuvent pas être initialisés par l'initialisation par défaut ou par leur initialiseur de membre par défaut , le cas échéant (depuis C++11) , tels que les membres de types référence et types qualifiés const, les initialiseurs de membres doivent être spécifiés. (Notez que les initialiseurs de membres par défaut pour les membres de données non statiques des instanciations de modèles de classe peuvent être invalides si le type du membre ou l'initialiseur est dépendant.) (depuis C++11) Aucune initialisation n'est effectuée pour les unions anonymes ou les membres variants qui n'ont pas d'initialiseur de membre ou d'initialiseur de membre par défaut (depuis C++11) .
Les initialiseurs où class nomme une classe de base virtuelle sont ignorés durant la construction de toute classe qui n'est pas la classe la plus dérivée de l'objet en cours de construction.
Les noms qui apparaissent dans initializer sont évalués dans la portée du constructeur :
class X { int a, b, i, j; public: const int& r; X(int i) : r(a) // initialise X::r pour référencer X::a , b{i} // initialise X::b à la valeur du paramètre i , i(i) // initialise X::i à la valeur du paramètre i , j(this->i) // initialise X::j à la valeur de X::i {} };
Les exceptions levées depuis les initialiseurs de membres peuvent être gérées par un bloc try de fonction .
|
Si un membre de données non statique possède un initialiseur de membre par défaut et apparaît également dans une liste d'initialisation de membre, alors l'initialiseur de membre est utilisé et l'initialiseur de membre par défaut est ignoré : struct S { int n = 42; // default member initializer S() : n(7) {} // will set n to 7, not 42 }; |
(depuis C++11) |
Les membres de référence ne peuvent pas être liés à des temporaires dans une liste d'initialisation de membre :
struct A { A() : v(42) {} // Erreur const int& v; };
Note : même chose pour default member initializer .
Opérations durant la construction et la destruction
Les fonctions membres (y compris les
fonctions membres virtuelles
) peuvent être appelées pour un objet en cours de construction ou de destruction. De même, un objet en cours de construction ou de destruction peut être l'opérande de
typeid
ou de
dynamic_cast
.
Cependant, si ces opérations sont effectuées lors de l'une des évaluations suivantes, le comportement est indéfini :
|
(depuis C++26) |
- une évaluation d'une liste d'initialisation de membre avant que tous les member-initializer s pour les classes de base ne soient terminés
Constructeur déléguéSi le nom de la classe elle-même apparaît comme class-or-identifier dans la liste d'initialisation des membres, alors la liste doit se composer de ce seul initialiseur de membre ; un tel constructeur est appelé constructeur délégué , et le constructeur sélectionné par le seul membre de la liste d'initialisation est le constructeur cible . Dans ce cas, le constructeur cible est sélectionné par résolution de surcharge et exécuté en premier, puis le contrôle revient au constructeur délégué et son corps est exécuté. Les constructeurs délégués ne peuvent pas être récursifs. class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int) }; Héritage des constructeursVoir using declaration . |
(depuis C++11) |
Ordre d'initialisation
L'ordre des initialiseurs de membres dans la liste est sans importance, l'ordre réel d'initialisation est le suivant :
(Note : si l'ordre d'initialisation était contrôlé par l'apparition dans les listes d'initialisation des membres de différents constructeurs, alors le destructeur ne pourrait pas garantir que l'ordre de destruction soit l'inverse de l'ordre de construction.)
Notes
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_delegating_constructors
|
200604L
|
(C++11) | Constructeurs délégués |
Exemple
#include <fstream> #include <string> #include <mutex> struct Base { int n; }; struct Class : public Base { unsigned char x; unsigned char y; std::mutex m; std::lock_guard<std::mutex> lg; std::fstream f; std::string s; Class(int x) : Base{123}, // initialiser la classe de base x(x), // x (membre) est initialisé avec x (paramètre) y{0}, // y initialisé à 0 f{"test.cc", std::ios::app}, // ceci se produit après l'initialisation de m et lg s(__func__), // __func__ est disponible car la liste d'initialisation fait partie du constructeur lg(m), // lg utilise m, qui est déjà initialisé m{} // m est initialisé avant lg même s'il apparaît en dernier ici {} // instruction composée vide Class(double a) : y(a + 1), x(y), // x sera initialisé avant y, sa valeur ici est indéterminée lg(m) {} // l'initialiseur de la classe de base n'apparaît pas dans la liste, il est // initialisé par défaut (pas la même chose que si Base() était utilisé, ce qui est une initialisation par valeur) Class() try // le bloc try de fonction commence avant le corps de la fonction, qui inclut la liste d'initialisation : Class(0.0) // constructeur délégué { // ... } catch (...) { // une exception s'est produite lors de l'initialisation } }; int main() { Class c; Class c1(1); Class c2(0.1); }
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 194 | C++98 |
la syntaxe du déclarateur de constructeur ne permettait
qu'au plus un spécificateur de fonction (par exemple, un constructeur ne pouvait pas être déclaré inline explicit ) |
plusieurs spécificateurs de fonction
autorisés |
| CWG 257 | C++98 |
il n'était pas spécifié si une classe abstraite devait
fournir des initialiseurs de membres pour ses classes de base virtuelles |
spécifié comme non requis
et ces initialiseurs de membres sont ignorés lors de l'exécution |
| CWG 263 | C++98 |
la syntaxe du déclarateur de constructeur
interdisait aux constructeurs d'être des amis |
autorise les constructeurs
à être des amis |
| CWG 1345 | C++98 |
les membres d'union anonymes sans initialiseurs
de membres par défaut étaient initialisés par défaut |
ils ne sont pas initialisés |
| CWG 1435 | C++98 |
la signification de "nom de classe" dans la
syntaxe du déclarateur de constructeur était peu claire |
modifié la syntaxe vers une syntaxe
de déclarateur de fonction spécialisée |
| CWG 1696 | C++98 |
les membres de référence pouvaient être initialisés avec des temporaires
(dont la durée de vie se terminait à la fin du constructeur) |
une telle initialisation
est incorrecte |
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 11.4.5 Constructeurs [class.ctor]
-
- 11.9.3 Initialisation des bases et membres [class.base.init]
- Norme C++20 (ISO/CEI 14882:2020) :
-
- 11.4.4 Constructeurs [class.ctor]
-
- 11.10.2 Initialisation des bases et membres [class.base.init]
- Norme C++17 (ISO/IEC 14882:2017) :
-
- 15.1 Constructeurs [class.ctor]
-
- 15.6.2 Initialisation des bases et membres [class.base.init]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 12.1 Constructeurs [class.ctor]
-
- 12.6.2 Initialisation des bases et membres [class.base.init]
- Norme C++11 (ISO/CEI 14882:2011) :
-
- 12.1 Constructeurs [class.ctor]
-
- 12.6.2 Initialisation des bases et des membres [class.base.init]
- Norme C++98 (ISO/CEI 14882:1998) :
-
- 12.1 Constructeurs [class.ctor]
-
- 12.6.2 Initialisation des bases et membres [class.base.init]