Namespaces
Variants

Constructors and member initializer lists

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 -

spécification d'exception dynamique

(jusqu'à C++11)

soit spécification d'exception dynamique
soit spécification noexcept

(depuis C++11)
(jusqu'à C++17)

spécification noexcept

(depuis C++17)
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 :

  • 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)
1) Initialise directement le membre de données nommé par member avec initializer . member ne peut nommer que des membres de données non statiques.
2) Initialise un objet de classe avec initializer . class ne peut désigner que les classes suivantes :
(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 .
3) Initialise plusieurs sous-objets de classe de base en utilisant une expansion de paquet .
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 constructeurs

Voir 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 :

1) Si le constructeur est pour la classe la plus dérivée, les bases virtuelles sont initialisées dans l'ordre dans lequel elles apparaissent dans le parcours en profondeur de gauche à droite des déclarations de classes de base (gauche à droite se réfère à l'apparition dans les listes de spécificateurs de base).
2) Ensuite, les bases directes sont initialisées dans l'ordre de gauche à droite tel qu'elles apparaissent dans la liste de spécificateurs de base de cette classe.
3) Ensuite, les membres de données non statiques sont initialisés dans l'ordre de déclaration dans la définition de la classe.
4) Enfin, le corps du constructeur est exécuté.

(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]

Voir aussi