Partager via


Initialiseurs

Un initialiseur spécifie la valeur initiale d'une variable. Vous pouvez initialiser des variables dans les contextes suivants :

  • Dans la définition d'une variable :

    int i = 3;
    Point p1{ 1, 2 };
    
  • Comme l'un des paramètres d'une fonction :

    set_point(Point{ 5, 6 });
    
  • En tant que valeur de retour d'une fonction :

    Point get_new_point(int x, int y) { return { x, y }; }
    Point get_new_point(int x, int y) { return Point{ x, y }; }
    

Les initialiseurs peuvent prendre les formes suivantes :

  • Une expression (ou une liste d'expressions séparées par des virgules) entre parenthèses :

    Point p1(1, 2);
    
  • Un signe égal suivi d'une expression :

    string s = "hello";
    
  • Une liste d'initialiseurs entre accolades. La liste peut être vide ou comprendre un ensemble de listes :

    struct Point{
        int x;
        int y;
    };
    class PointConsumer{
    public:
        void set_point(Point p){};
        void set_points(initializer_list<Point> my_list){};
    };
    int main() {
        PointConsumer pc{};
        pc.set_point({});
        pc.set_point({ 3, 4 });
        pc.set_points({ { 3, 4 }, { 5, 6 } });
    }
    

Types d'initialisations

Il existe plusieurs types d'initialisations, qui peuvent se produire à différents moments durant l'exécution du programme. Les différents types d'initialisations ne s'excluent pas mutuellement. Par exemple, l'initialisation de listes peut déclencher l'initialisation de valeurs et dans d'autres cas, l'initialisation d'agrégats.

Initialisation à zéro

L'initialisation à zéro attribue à une variable une valeur zéro convertie implicitement en type :

  • Les variables numériques sont initialisées à 0 (ou 0,0, ou 0,0000000000, etc.).

  • Les variables de type caractère sont initialisées à '\0'.

  • Les pointeurs sont initialisés à nullptr.

  • Les membres des tableaux, des classes POD, des structs et des unions sont initialisés à zéro.

L'initialisation à zéro peut être effectuée à différents moments :

  • Au démarrage du programme, pour toutes les variables nommées dont la durée est statique. Ces variables pourront être initialisées à nouveau.

  • Pendant l'initialisation de valeurs, pour les types scalaires et ceux de classe POD qui sont initialisés à l'aide d'accolades vides.

  • Pour les tableaux dont seul un sous-ensemble de leurs membres est initialisé.

Voici quelques exemples d'initialisation à zéro :

struct my_struct{
    int i;
    char c;
};

int i0;              // zero-initialized to 0
int main() {
    static float f1;  // zero-initialized to 0.000000000
    double d{};     // zero-initialized to 0.00000000000000000
    int* ptr{};     // initialized to nullptr
    char s_array[3]{'a', 'b'};  // the third char is initialized to '\0'
    int int_array[5] = { 8, 9, 10 };  // the fourth and fifth ints are initialized to 0
    my_struct a_struct{};   // i = 0, c = '\0'
}

Initialisation par défaut

L'initialisation par défaut des classes, des structs et des unions utilise un constructeur par défaut. Le constructeur par défaut peut être appelé en n'incluant pas d'expression d'initialisation ou en utilisant le mot clé new :

MyClass mc1;
MyClass* mc3 = new MyClass;

Si la classe, le struct ou l'union n'a pas de constructeur par défaut, le compilateur renvoie une erreur.

Les variables scalaires sont initialisées par défaut lorsqu'elles sont définies sans expression d'initialisation. Leurs valeurs sont indéterminées.

int i1;
float f;
char c;

Les tableaux sont initialisés par défaut lorsqu'ils sont définis sans expression d'initialisation. Lorsqu'un tableau est initialisé par défaut, ses membres sont initialisés par défaut et ont des valeurs indéterminées :

int int_arr[3];

Si les membres du tableau n'ont pas de constructeur par défaut, le compilateur renvoie une erreur.

Initialisation par défaut des variables constantes

Les variables constantes doivent être déclarées en même temps que l'initialiseur. Si elles sont de type scalaire, elles entraîneront une erreur du compilateur. Si elles sont de type de classe avec un constructeur par défaut, elles entraîneront un avertissement :

class MyClass{};
int main() {
    //const int i2;   // compiler error C2734: const object must be initialized if not extern
    //const char c2;  // same error
    const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}

Initialisation par défaut des variables statiques

Les variables statiques déclarées sans initialiseur sont initialisées à 0 (implicitement converties en type) :

class MyClass {   
private:
    int m_int;
    char m_char;
};

int main() {
    static int int1;       // 0
    static char char1;     // '\0'
    static bool bool1;   // false
    static MyClass mc1;     // {0, '\0'}
}

Pour plus d'informations sur l'initialisation d'objets statiques globaux, consultez Considérations supplémentaires sur le démarrage.

Initialisation de valeurs

L'initialisation de valeurs se produit dans les cas suivants :

  • Une valeur nommée est initialisée à l'aide d'accolades vides.

  • Un objet temporaire anonyme est initialisé à l'aide de parenthèses ou d'accolades vides.

  • Un objet est initialisé à l'aide du mot clé new et de parenthèses ou d'accolades vides.

L'initialisation de valeurs entraîne ce qui suit :

  • Pour les classes qui possèdent au moins un constructeur public, le constructeur par défaut est appelé.

  • Pour les classes (autres que les classes d'union) qui n'ont pas déclaré de constructeur, l'objet est initialisé à zéro et le constructeur par défaut est appelé.

  • Pour les tableaux, chaque élément est initialisé par une valeur.

  • Dans tous les autres cas, la variable est initialisée à zéro.

class BaseClass {  
private:
    int m_int;
};

int main() {
    BaseClass bc{};     // class is initialized
    BaseClass*  bc2 = new BaseClass();  // class is initialized, m_int value is 0
    int int_arr[3]{};  // value of all members is 0
    int a{};     // value of a is 0
    double b{};  // value of b is 0.00000000000000000
}

Initialisation de copie

L'initialisation de copie correspond à l'initialisation d'un objet à l'aide d'un autre objet. Elle se produit dans les cas suivants :

  • Une variable est initialisée à l'aide d'un signe égal.

  • Un argument est passé à une fonction.

  • Un objet est retourné par une fonction.

  • Une exception est levée ou interceptée.

  • Des données membres non static sont initialisées à l'aide d'un signe égal.

  • Les membres des classes, des structs et des unions sont initialisés par initialisation de copie pendant l'initialisation des agrégats. Pour obtenir des exemples, consultez Initialisation d'agrégats.

Ce code montre des exemples d'initialisation de copie :

#include <iostream>
using namespace std;

class MyClass{
public:
    MyClass(int myInt) {}
    void set_int(int myInt) { m_int = myInt; }
    int get_int() const { return m_int; }
private:
    int m_int = 7; // copy initialization of m_int

};
class MyException : public exception{};
int main() {
    int i = 5;              // copy initialization of i
    MyClass mc1{ i };
    MyClass mc2 = mc1;      // copy initialization of mc2 from mc1
    MyClass mc1.set_int(i);    // copy initialization of parameter from i
    int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()

    try{
        throw MyException();    
    }
    catch (MyException ex){ // copy initialization of ex
        cout << ex.what();  
    }
}

L'initialisation de copie ne peut pas appeler de constructeurs explicites :

vector<int> v = 10; // the constructor is explicit; compiler error C2440: cannot convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error

Dans certains cas, si le constructeur de copie de la classe est supprimé ou inaccessible, l'initialisation de copie entraîne une erreur du compilateur. Pour plus d'informations, consultez Initialisation explicite.

Initialisation directe

L'initialisation directe utilise des accolades ou des parenthèses non vides. Contrairement à l'initialisation de copie, elle peut appeler des constructeurs explicites. Elle se produit dans les cas suivants :

  • Une variable est initialisée à l'aide d'accolades ou de parenthèses non vides.

  • Une variable est initialisée à l'aide du mot clé new et d'accolades ou de parenthèses non vides.

  • Une variable est initialisée à l'aide de static_cast.

  • Dans un constructeur, les classes de base et les membres non static sont initialisés à l'aide d'une liste d'initialiseurs.

  • Dans la copie d'une variable capturée d'une expression lambda.

Voici quelques exemples d'initialisation directe :

class BaseClass{
public:
    BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
    int m_int;
};

class DerivedClass : public BaseClass{
public:
    // BaseClass and m_char are direct initialized
    DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
    char m_char;
};
int main(){
    BaseClass bc1(5);
    DerivedClass dc1{ 1, 'c' };
    BaseClass* bc2 = new BaseClass(7);
    BaseClass bc3 = static_cast<BaseClass>(dc1);

    int a = 1;
    function<int()> func = [a](){  return a + 1; }; // a is direct initialized
    int n = func();
}

Initialisation de liste

L'initialisation de liste se produit lorsqu'une variable est initialisée à l'aide d'une liste d'initialiseurs entre accolades. Les listes d'initialiseurs entre accolades peuvent être utilisées dans les cas suivants :

  • Une variable est initialisée.

  • Une classe est initialisée à l'aide du mot clé new.

  • Un objet est retourné par une fonction.

  • Un argument est passé à une fonction.

  • L'un des arguments est compris dans une initialisation directe.

  • Dans un initialiseur de données membres non static.

  • Dans une liste d'initialiseurs de constructeur.

Exemples d'initialisation de liste :

class MyClass {
public:
    MyClass(int myInt, char myChar) {}  
private:
    int m_int[]{ 3 };
    char m_char;
};
class MyClassConsumer{
public:
    void set_class(MyClass c) {}
    MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
    int my_int;
    char my_char;
    MyClass my_class;
};
int main() {
    MyClass mc1{ 1, 'a' };
    MyClass* mc2 = new MyClass{ 2, 'b' };
    MyClass mc3 = { 3, 'c' };

    MyClassConsumer mcc;
    mcc.set_class(MyClass{ 3, 'c' });
    mcc.set_class({ 4, 'd' });

    MyStruct ms1{ 1, 'a', { 2, 'b' } };
}

Initialisation d'agrégats

L'initialisation d'agrégats est un type d'initialisation de liste utilisé pour les tableaux et les types de classes (souvent des structs ou des unions) qui n'ont :

  • Aucun membre privé ou protégé

  • Aucun constructeur fourni par l'utilisateur, à l'exception des constructeurs supprimés ou utilisés par défaut de manière explicite

  • Aucune classe de base

  • Aucune fonction membre virtuelle

  • Aucun initialiseur avec accolades ou signe égal pour les membres non static

Les initialiseurs d'agrégats se composent d'une liste d'initialisation entre accolades, avec ou sans signe égal :

#include <iostream>
using namespace std;

struct MyAggregate{
    int myInt;
    char myChar;
};

int main() {
    MyAggregate agg1{ 1, 'c' };

    cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
    cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;

    int myArr1[]{ 1, 2, 3, 4 };
    int myArr2[3] = { 5, 6, 7 };
    int myArr3[5] = { 8, 9, 10 };

    cout << "myArr1: ";
    for (int i : myArr1){
        cout << i << " ";
    }
    cout << endl;
    
    cout << "myArr3: ";
    for (auto const &i : myArr3) {
        cout << i << " ";
    }
    cout << endl;
}

Voici la sortie :

agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0

Important

Les membres de tableau qui ont fait l'objet d'une déclaration, mais pas d'une initialisation explicite pendant l'initialisation d'agrégats sont initialisés à zéro, comme dans myArr3.

Initialisation des unions et des structs

Si une union n'a pas de constructeur, vous pouvez l'initialiser à l'aide d'une valeur (ou d'une autre instance d'union). La valeur est utilisée pour initialiser le premier champ non static. C'est donc différent de l'initialisation de struct, durant laquelle la première valeur de l'initialiseur est utilisée pour initialiser le premier champ, la deuxième pour initialiser le deuxième champ, et ainsi de suite. L'exemple suivant vous permet de comparer l'initialisation des unions et des structs :

struct MyStruct {
    int myInt;
    char myChar;
};
union MyUnion {
    int my_int;
    char my_char;
    bool my_bool;
    MyStruct my_struct;
};

int main() {  
    MyUnion mu1{ 'a' };  // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
    MyUnion mu2{ 1 };   // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
    MyUnion mu3{};      // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    MyUnion mu4 = mu3;  // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    //MyUnion mu5{ 1, 'a', true };  // compiler error: C2078: too many initializers
    //MyUnion mu6 = 'a';            // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
    //MyUnion mu7 = 1;              // compiler error: C2440: cannot convert from 'int' to 'MyUnion'

    MyStruct ms1{ 'a' };            // myInt = 97, myChar = '\0'
    MyStruct ms2{ 1 };              // myInt = 1, myChar = '\0'
    MyStruct ms3{};                 // myInt = 0, myChar = '\0'
    MyStruct ms4{1, 'a'};           // myInt = 1, myChar = 'a'
    MyStruct ms5 = { 2, 'b' };      // myInt = 2, myChar = 'b'
}

Initialisation d'agrégats contenant des agrégats

Les types d'agrégats peuvent contenir d'autres types d'agrégats. Par exemple, les tableaux peuvent contenir des tableaux, des structs, etc. Ces types sont initialisés à l'aide de jeux imbriqués d'accolades :

struct MyStruct {
    int myInt;
    char myChar;
};
int main() {
    int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
    int intArr3[2][2] = {1, 2, 3, 4};  
    MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}

Initialisation de références

Pour plus d'informations sur l'initialisation de références, consultez Initialisation des références.

Initialisation des variables externes

Les déclarations de variables automatiques, de registre, statiques et externes peuvent contenir des initialiseurs. Toutefois, les déclarations de variables externes ne peuvent contenir des initialiseurs que si les variables ne sont pas déclarées comme extern. Pour plus d'informations, consultez External.

Voir aussi

Référence

Déclarateurs