La liste d’initialisation est utilisée pour initialiser les données membres d’une classe. La liste des membres à initialiser est indiquée par le constructeur sous la forme d'une liste séparée par des virgules suivie de deux points. Voici un exemple qui utilise la liste d'initialisation pour initialiser x et y de la classe Point.
Exemple
C++
#include> using> namespace> std;> class> Point {> private>:> >int> x;> >int> y;> public>:> >Point(>int> i = 0,>int> j = 0): x(i), y(j) {}> >/* The above use of Initializer list is optional as the> >constructor can also be written as:> >Point(int i = 0, int j = 0) {> >x = i;> >y = j;> >}> >*/> >int> getX()>const> {>return> x; }> >int> getY()>const> {>return> y; }> };> int> main()> {> >Point t1(10, 15);> >cout <<>'x = '> << t1.getX() <<>', '>;> >cout <<>'y = '> << t1.getY();> >return> 0;> }> |
>
>Sortir
x = 10, y = 15>
Le code ci-dessus n'est qu'un exemple de syntaxe de la liste Initializer. Dans le code ci-dessus, x et y peuvent également être facilement initialisés dans le constructeur. Mais il existe des situations dans lesquelles l'initialisation des données membres à l'intérieur du constructeur ne fonctionne pas et la liste d'initialisation doit être utilisée. Voici de tels cas :
1. Pour l'initialisation des membres de données const non statiques
Les membres de données const doivent être initialisés à l’aide de Initializer List. Dans l'exemple suivant, t est un membre de données const de la classe Test et est initialisé à l'aide de Initializer List. La raison de l'initialisation du membre de données const dans la liste d'initialisation est qu'aucune mémoire n'est allouée séparément pour le membre de données const, elle est repliée dans la table des symboles, raison pour laquelle nous devons l'initialiser dans la liste d'initialisation.
De plus, il s'agit d'un constructeur paramétré et nous n'avons pas besoin d'appeler l'opérateur d'affectation, ce qui signifie que nous évitons une opération supplémentaire.
Exemple
C++
// C++ progmram to demonstrate the use of> // initializer list to initialize the const> // data member> #include> using> namespace> std;> class> Test {> >const> int> t;> public>:> >//Initializer list must be used> >Test(>int> t):t(t) {}> >int> getT() {>return> t; }> };> int> main() {> >Test t1(10);> >cout< return 0; }> |
>
>Sortir
10>
2. Pour l'initialisation des membres de référence
Les membres de référence doivent être initialisés à l’aide de la liste d’initialisation. Dans l'exemple suivant, t est un membre de référence de la classe Test et est initialisé à l'aide de la liste d'initialisation.
Exemple
C++
arbre de recherche binaire]
// Initialization of reference data members> #include> using> namespace> std;> class> Test {> >int> &t;> public>:> >Test(>int> &t):t(t) {}>//Initializer list must be used> >int> getT() {>return> t; }> };> int> main() {> >int> x = 20;> >Test t1(x);> >cout< x = 30; cout< return 0; }> |
>
>Sortir
20 30>
3. Pour l'initialisation des objets membres qui n'ont pas de constructeur par défaut
Dans l’exemple suivant, un objet a de classe A est un membre de données de classe B et A n’a pas de constructeur par défaut. La liste d’initialisation doit être utilisée pour initialiser un.
Exemple
C++
// C++ progmam to initialize a member object without default> // constructor> #include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int>);> };> A::A(>int> arg)> {> >i = arg;> >cout <<>'A's Constructor called: Value of i: '> << i> ><< endl;> }> // Class B contains object of A> class> B {> >A a;> public>:> >B(>int>);> };> B::B(>int> x) : a(x)> {>// Initializer list must be used> >cout <<>'B's Constructor called'>;> }> int> main()> {> >B obj(10);> >return> 0;> }> |
>
>Sortir
A's Constructor called: Value of i: 10 B's Constructor called>
Si la classe A avait à la fois des constructeurs par défaut et paramétrés, alors Initializer List n'est pas indispensable si nous voulons initialiser a en utilisant le constructeur par défaut, mais il est indispensable d'initialiser a en utilisant le constructeur paramétré.
4. Pour l'initialisation des membres du groupe de base
Comme au point 3, le constructeur paramétré de la classe de base ne peut être appelé qu'à l'aide de la liste Initializer.
Exemple
C++
#include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int> );> };> A::A(>int> arg) {> >i = arg;> >cout <<>'A's Constructor called: Value of i: '> << i << endl;> }> // Class B is derived from A> class> B: A {> public>:> >B(>int> );> };> B::B(>int> x):A(x) {>//Initializer list must be used> >cout <<>'B's Constructor called'>;> }> int> main() {> >B obj(10);> >return> 0;> }> |
>
>Sortir
A's Constructor called: Value of i: 10 B's Constructor called>
5. Lorsque le nom du paramètre du constructeur est le même que celui du membre de données
Si le nom du paramètre du constructeur est le même que le nom du membre de données, le membre de données doit être initialisé soit en utilisant ce pointeur ou Liste d'initialisation. Dans l’exemple suivant, le nom du membre et le nom du paramètre pour A() sont i.
Exemple
C++
#include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int>);> >int> getI()>const> {>return> i; }> };> A::A(>int> i) : i(i)> {> }>// Either Initializer list or this pointer must be used> /* The above constructor can also be written as> A::A(int i) {> >this->je = je;> }> */> int> main()> {> >A a(10);> >cout << a.getI();> >return> 0;> }> |
>
>Sortir
10>
6. Pour des raisons de performances
Il est préférable d'initialiser toutes les variables de classe dans la liste d'initialisation au lieu d'attribuer des valeurs à l'intérieur du corps. Prenons l'exemple suivant :
Exemple
C++
// Without Initializer List> class> MyClass {> >Type variable;> public>:> >MyClass(Type a) {>// Assume that Type is an already> >// declared class and it has appropriate> >// constructors and operators> >variable = a;> >}> };> |
>
>
Ici, le compilateur suit les étapes suivantes pour créer un objet de type MyClass
1. Le constructeur de type est appelé en premier pour a.
2. Variable de construction par défaut
3. L'opérateur d'affectation de Type est appelé dans le corps du constructeur MyClass() pour attribuer
variable = a;>
4. Et puis enfin, le destructeur de Type est appelé car il est hors de portée.
Considérons maintenant le même code avec le constructeur MyClass() avec Initializer List
C++
gimp changer de couleur
// With Initializer List> class> MyClass {> >Type variable;> public>:> >MyClass(Type a):variable(a) {>// Assume that Type is an already> >// declared class and it has appropriate> >// constructors and operators> >}> };> |
>
>
Avec la liste d'initialisation, les étapes suivantes sont suivies par le compilateur :
1. Le constructeur de type est appelé en premier pour a.
2. Le constructeur paramétré de la classe Type est appelé pour initialiser : variable(a). Les arguments de la liste d'initialisation sont utilisés pour copier directement la variable de construction.
3. Le destructeur de Type est appelé pour a car il est hors de portée.
Comme nous pouvons le voir dans cet exemple, si nous utilisons l'affectation à l'intérieur du corps du constructeur, il y a trois appels de fonction : constructeur + destructeur + un appel d'opérateur d'affectation d'addition. Et si nous utilisons Initializer List, il n'y a que deux appels de fonction : constructeur de copie + appel de destructeur. Voir cet article pour un exemple concret sur ce point.
Cette pénalité d'affectation sera bien plus importante dans les applications réelles où il y aura de nombreuses variables de ce type. Grâce à ptr pour avoir ajouté ce point.
Paramètre vs initialisation uniforme en C++
Il est préférable d'utiliser une liste d'initialisation avec une initialisation uniforme {} plutôt qu'une initialisation de paramètre () pour éviter le problème de conversions restreintes et de comportement inattendu. Il fournit une vérification de type plus stricte lors de l'initialisation et empêche les conversions potentielles restrictives.
Code utilisant l'initialisation des paramètres ()
C++
#include> class> Base {> >char> x;> public>:> >Base(>char> a)> >: x{ a }> >{> >}> >void> print() { std::cout <<>static_cast><>int>>(X); }> };> int> main()> {> >Base b{ 300 };>// Using uniform initialization with {}> >b.print();> >return> 0;> }> |
>
>Sortir
44>
Dans le code ci-dessus, la valeur 300 est hors de la plage valide pour char, ce qui peut conduire à un comportement indéfini et à des résultats potentiellement incorrects. Le compilateur peut générer un avertissement ou une erreur pour cette situation, en fonction des paramètres de compilation.
Code utilisant l'initialisation uniforme {}
En utilisant une initialisation uniforme avec {} et en initialisant x avec la valeur fournie a, le compilateur effectuera une vérification de type plus stricte et émettra un avertissement ou une erreur lors de la compilation, indiquant la conversion restreinte de int en char.
Voici du code avec une initialisation uniforme {} , qui entraîne un avertissement et donc mieux à utiliser
C++
#include> class> Base {> >char> x;> public>:> >Base(>char> a)> >: x{ a }> >{> >}> >void> print() { std::cout <<>static_cast><>int>>(X); }> };> int> main()> {> >Base b{ 300 };>// Using uniform initialization with {}> >b.print();> >return> 0;> }> |
>
>
main.cpp: In function ‘int main()’: main.cpp:17:17: error: narrowing conversion of ‘300’ from ‘int’ to ‘char’ [-Wnarrowing] 17 | Base b{ 300 }; // Using uniform initialization with {} | ^>