logo

Kdy používáme seznam inicializátorů v C++?

Seznam inicializátorů se používá při inicializaci datových členů třídy. Seznam členů, který se má inicializovat, je označen konstruktorem jako seznam oddělený čárkou následovaný dvojtečkou. Následuje příklad, který používá seznam inicializátorů k inicializaci x a y třídy Point.

Příklad



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



>

>

Výstup

x = 10, y = 15>

Výše uvedený kód je pouze příkladem syntaxe seznamu inicializátorů. Ve výše uvedeném kódu lze také snadno inicializovat x a y uvnitř konstruktoru. Existují však situace, kdy inicializace datových členů uvnitř konstruktoru nefunguje a je třeba použít seznam inicializátorů. Jsou to následující případy:

1. Pro inicializaci nestatických konst datových členů

datové členy const musí být inicializovány pomocí seznamu inicializátorů. V následujícím příkladu je t konstantní datový člen třídy Test a je inicializován pomocí seznamu inicializátorů. Důvodem pro inicializaci const datového členu v seznamu inicializátoru je to, že žádná paměť není alokována samostatně pro const datový člen, je složený v tabulce symbolů, kvůli čemuž jej musíme inicializovat v seznamu inicializátoru.

Je to také parametrizovaný konstruktor a nemusíme volat operátor přiřazení, což znamená, že se vyhneme jedné operaci navíc.

Příklad

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

>

>

Výstup

10>

2. Pro inicializaci referenčních členů

Referenční členy musí být inicializovány pomocí seznamu inicializátorů. V následujícím příkladu je t referenčním členem třídy Test a je inicializován pomocí seznamu inicializátorů.

Příklad

C++


příklad třídy java



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

>

>

Výstup

20 30>

3. Pro inicializaci členských objektů, které nemají výchozí konstruktor

V následujícím příkladu je objekt a třídy A datovým členem třídy B a A nemá výchozí konstruktor. K inicializaci je nutné použít seznam inicializátorů.

Příklad

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

>

>

Výstup

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

Pokud třída A měla výchozí i parametrizované konstruktory, pak seznam inicializátorů není nutností, pokud chceme inicializovat a pomocí výchozího konstruktoru, ale je nutné inicializovat a pomocí parametrizovaného konstruktoru.

4. Pro inicializaci členů základní třídy

Stejně jako bod 3 lze parametrizovaný konstruktor základní třídy volat pouze pomocí seznamu inicializátorů.

Příklad

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

>

>

Výstup

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

5. Když je název parametru konstruktoru stejný jako datový člen

Pokud je název parametru konstruktoru stejný jako název datového členu, musí být datový člen inicializován buď pomocí tento ukazatel nebo Seznam inicializátorů. V následujícím příkladu je název člena i název parametru pro A() i.

Příklad

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

>

>

Výstup

10>

6. Z důvodů výkonu

Je lepší inicializovat všechny proměnné třídy v seznamu Initializer List namísto přiřazování hodnot uvnitř těla. Zvažte následující příklad:

Příklad

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

>

>

Zde kompilátor podle následujících kroků vytvoří objekt typu MyClass

1. Konstruktor typu je volán nejprve pro a.

2. Výchozí proměnná konstrukce

3. Operátor přiřazení Type se nazývá uvnitř těla konstruktoru MyClass() k přiřazení

variable = a;>

4. A nakonec je zavolán destruktor typu a, protože jde mimo rozsah.

Nyní zvažte stejný kód s konstruktorem MyClass() se seznamem inicializátorů

C++


pokladna s git



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

>

>

Se seznamem inicializátorů kompilátor následuje následující kroky:

1. Konstruktor typu je volán nejprve pro a.
2. Parametrizovaný konstruktor třídy Type je volán k inicializaci: variable(a). Argumenty v seznamu inicializátoru se používají k přímému kopírování proměnné konstrukce.
3. Destruktor typu je volán pro a, protože jde mimo rozsah.

Jak můžeme vidět z tohoto příkladu, pokud použijeme přiřazení uvnitř těla konstruktoru, existují tři volání funkce: konstruktor + destruktor + jedno volání operátoru přiřazení sčítání. A pokud použijeme seznam inicializátorů, existují pouze dvě volání funkcí: konstruktor kopírování + volání destruktoru. Podívejte se na tento příspěvek pro běžící příklad v tomto bodě.

Tato penalizace přiřazení bude mnohem více v reálných aplikacích, kde bude takových proměnných mnoho. Díky ptr za přidání tohoto bodu.

Parametr vs jednotná inicializace v C++

Je lepší použít inicializační seznam s jednotnou inicializací {} spíše než inicializaci parametrů (), abyste se vyhnuli problému zúžení konverzí a neočekávaného chování. Poskytuje přísnější kontrolu typu během inicializace a zabraňuje potenciálním zužujícím se konverzím

Kód pomocí inicializace parametru ()

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

>

>

Výstup

44>

Ve výše uvedeném kódu je hodnota 300 mimo platný rozsah pro znak, což může vést k nedefinovanému chování a potenciálně nesprávným výsledkům. Kompilátor může pro tuto situaci vygenerovat varování nebo chybu v závislosti na nastavení kompilace.

Kód pomocí jednotné inicializace {}

Použitím jednotné inicializace s {} a inicializací x s poskytnutou hodnotou a, kompilátor provede přísnější typovou kontrolu a během kompilace vydá varování nebo chybu, která indikuje zúžení převodu z int na char.
Zde je kód s jednotnou inicializací {} , která má za následek varování a tudíž lepší použití

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