logo

Wann verwenden wir die Initialisierungsliste in C++?

Die Initialisierungsliste wird zum Initialisieren der Datenelemente einer Klasse verwendet. Die Liste der zu initialisierenden Mitglieder wird mit dem Konstruktor als durch Kommas getrennte Liste gefolgt von einem Doppelpunkt angegeben. Es folgt ein Beispiel, das die Initialisierungsliste verwendet, um x und y der Point-Klasse zu initialisieren.

Beispiel



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;> }>



>

>

Ausgabe

x = 10, y = 15>

Der obige Code ist nur ein Beispiel für die Syntax der Initializer-Liste. Im obigen Code können x und y auch einfach im Konstruktor initialisiert werden. Es gibt jedoch Situationen, in denen die Initialisierung von Datenelementen im Konstruktor nicht funktioniert und die Initialisierungsliste verwendet werden muss. Folgende Fälle sind solche Fälle:

1. Zur Initialisierung nichtstatischer const-Datenelemente

const-Datenelemente müssen mithilfe der Initialisierungsliste initialisiert werden. Im folgenden Beispiel ist t ein konstantes Datenelement der Testklasse und wird mithilfe der Initialisierungsliste initialisiert. Der Grund für die Initialisierung des const-Datenelements in der Initialisierungsliste besteht darin, dass für das const-Datenelement kein Speicher separat zugewiesen wird. Es ist in der Symboltabelle gefaltet, weshalb wir es in der Initialisierungsliste initialisieren müssen.

Außerdem handelt es sich um einen parametrisierten Konstruktor und wir müssen den Zuweisungsoperator nicht aufrufen, was bedeutet, dass wir eine zusätzliche Operation vermeiden.

Beispiel

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; }>

>

>

Ausgabe

10>

2. Zur Initialisierung von Referenzmitgliedern

Referenzelemente müssen mithilfe der Initialisierungsliste initialisiert werden. Im folgenden Beispiel ist t ein Referenzmitglied der Testklasse und wird mithilfe der Initialisierungsliste initialisiert.

Beispiel

C++


binärer Suchalgorithmus



// 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; }>

>

>

Ausgabe

20 30>

3. Zur Initialisierung von Mitgliedsobjekten, die keinen Standardkonstruktor haben

Im folgenden Beispiel ist ein Objekt a der Klasse A ein Datenelement der Klasse B und A verfügt über keinen Standardkonstruktor. Zur Initialisierung von a muss die Initialisierungsliste verwendet werden.

Beispiel

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;> }>

>

>

Ausgabe

A's Constructor called: Value of i: 10 B's Constructor called>

Wenn Klasse A sowohl Standard- als auch parametrisierte Konstruktoren hätte, ist die Initialisierungsliste kein Muss, wenn wir a mit dem Standardkonstruktor initialisieren möchten, aber sie ist ein Muss, um a mit dem parametrisierten Konstruktor zu initialisieren.

4. Zur Initialisierung von Basisklassenmitgliedern

Wie Punkt 3 kann der parametrisierte Konstruktor der Basisklasse nur über die Initializer List aufgerufen werden.

Beispiel

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;> }>

>

>

Ausgabe

A's Constructor called: Value of i: 10 B's Constructor called>

5. Wenn der Parametername des Konstruktors mit dem Datenmitglied übereinstimmt

Wenn der Parametername des Konstruktors mit dem Namen des Datenelements übereinstimmt, muss das Datenelement entweder mit initialisiert werden dieser Zeiger oder Initialisierungsliste. Im folgenden Beispiel lauten sowohl der Mitgliedsname als auch der Parametername für A() i.

Beispiel

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->i = i;> }> */> int> main()> {> >A a(10);> >cout << a.getI();> >return> 0;> }>

>

>

Ausgabe

10>

6. Aus Leistungsgründen

Es ist besser, alle Klassenvariablen in der Initialisierungsliste zu initialisieren, anstatt Werte innerhalb des Körpers zuzuweisen. Betrachten Sie das folgende Beispiel:

Beispiel

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;> >}> };>

>

>

Hier führt der Compiler die folgenden Schritte aus, um ein Objekt vom Typ MyClass zu erstellen

Sonderzeichenname

1. Der Konstruktor des Typs wird zuerst für a aufgerufen.

2. Standardkonstruktvariable

3. Der Zuweisungsoperator von Type wird innerhalb des zuzuweisenden MyClass()-Konstruktors aufgerufen

variable = a;>

4. Und schließlich wird der Destruktor von Type für a aufgerufen, da er den Gültigkeitsbereich verlässt.

Betrachten Sie nun denselben Code mit dem MyClass()-Konstruktor und der Initialisierungsliste

C++




// 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> >}> };>

>

>

Mit der Initialisierungsliste werden vom Compiler die folgenden Schritte ausgeführt:

1. Der Konstruktor des Typs wird zuerst für a aufgerufen.
2. Der parametrisierte Konstruktor der Typklasse wird aufgerufen, um Folgendes zu initialisieren: Variable(a). Die Argumente in der Initialisierungsliste werden verwendet, um die Konstruktvariable direkt zu kopieren.
3. Der Destruktor von Type wird für a aufgerufen, da er den Gültigkeitsbereich verlässt.

Wie wir in diesem Beispiel sehen können, gibt es drei Funktionsaufrufe, wenn wir die Zuweisung innerhalb des Konstruktorkörpers verwenden: Konstruktor + Destruktor + ein Additionszuweisungsoperatoraufruf. Und wenn wir die Initialisierungsliste verwenden, gibt es nur zwei Funktionsaufrufe: Kopierkonstruktor + Destruktoraufruf. In diesem Beitrag finden Sie ein laufendes Beispiel zu diesem Punkt.

Dieser Zuweisungsnachteil wird in realen Anwendungen, in denen es viele solcher Variablen gibt, viel höher ausfallen. Dank an ptr für das Hinzufügen dieses Punktes.

Parameter vs. einheitliche Initialisierung in C++

Es ist besser, eine Initialisierungsliste mit einheitlicher Initialisierung {} anstelle der Parameterinitialisierung () zu verwenden, um das Problem einschränkender Konvertierungen und unerwartetes Verhalten zu vermeiden. Es bietet eine strengere Typprüfung während der Initialisierung und verhindert mögliche einschränkende Konvertierungen

Code mit Parameterinitialisierung ()

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;> }>

>

>

Ausgabe

44>

Im obigen Code liegt der Wert 300 außerhalb des gültigen Bereichs für char, was zu undefiniertem Verhalten und möglicherweise falschen Ergebnissen führen kann. Abhängig von den Kompilierungseinstellungen generiert der Compiler in dieser Situation möglicherweise eine Warnung oder einen Fehler.

Code mit einheitlicher Initialisierung {}

Durch die Verwendung einer einheitlichen Initialisierung mit {} und die Initialisierung von x mit dem bereitgestellten Wert a führt der Compiler eine strengere Typprüfung durch und gibt während der Kompilierung eine Warnung oder einen Fehler aus, der auf die einschränkende Konvertierung von int nach char hinweist.
Hier ist Code mit einheitlicher Initialisierung {} , der zu einer Warnung führt und daher besser zu verwenden ist

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 {} | ^>