logo

Kedy používame zoznam inicializátorov v C++?

Zoznam inicializátorov sa používa na inicializáciu údajových členov triedy. Zoznam členov, ktorý sa má inicializovať, je označený konštruktorom ako zoznam oddelený čiarkou, za ktorým nasleduje dvojbodka. Nasleduje príklad, ktorý používa zoznam inicializátorov na inicializáciu x a y triedy Point.

Prí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ýkon

x = 10, y = 15>

Vyššie uvedený kód je len príkladom syntaxe zoznamu inicializátorov. Vo vyššie uvedenom kóde môžu byť x a y jednoducho inicializované vo vnútri konštruktora. Existujú však situácie, keď inicializácia údajových členov vo vnútri konštruktora nefunguje a je potrebné použiť zoznam inicializátorov. Ide o tieto prípady:

1. Pre inicializáciu nestatických členov konštantných údajov

Údaje const musia byť inicializované pomocou zoznamu inicializátorov. V nasledujúcom príklade t je konštantný dátový člen triedy Test a je inicializovaný pomocou zoznamu inicializátorov. Dôvodom inicializácie const dátového člena v zozname inicializátora je, že pre const dátový člen nie je alokovaná žiadna pamäť samostatne, je zložený v tabuľke symbolov, kvôli čomu ho musíme inicializovať v zozname inicializátorov.

Je to tiež parametrizovaný konštruktor a nemusíme volať operátor priradenia, čo znamená, že sa vyhneme jednej operácii navyše.

Prí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ýkon

10>

2. Pre inicializáciu referenčných členov

Referenčné členy musia byť inicializované pomocou zoznamu inicializátorov. V nasledujúcom príklade je t referenčným členom triedy Test a inicializuje sa pomocou zoznamu inicializátorov.

Príklad

C++


haldy triediť



// 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ýkon

20 30>

3. Pre inicializáciu členských objektov, ktoré nemajú predvolený konštruktor

V nasledujúcom príklade je objekt a triedy A dátovým členom triedy B a A nemá predvolený konštruktor. Zoznam inicializátorov sa musí použiť na inicializáciu a.

Prí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ýkon

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

Ak trieda A mala predvolené aj parametrizované konštruktory, potom zoznam inicializátorov nie je nutnosťou, ak chceme inicializovať pomocou predvoleného konštruktora, ale je nutné inicializovať pomocou parametrizovaného konštruktora.

4. Na inicializáciu členov základnej triedy

Podobne ako v bode 3, parametrizovaný konštruktor základnej triedy možno volať iba pomocou zoznamu inicializátorov.

Prí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ýkon

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

5. Keď je názov parametra konštruktéra rovnaký ako údajový člen

Ak je názov parametra konštruktora rovnaký ako názov údajového člena, potom musí byť údajový člen inicializovaný buď pomocou tento ukazovateľ alebo Zoznam inicializátorov. V nasledujúcom príklade je názov člena aj názov parametra pre A() i.

Prí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ýkon

10>

6. Z dôvodov výkonu

Je lepšie inicializovať všetky premenné triedy v zozname inicializátorov namiesto priraďovania hodnôt v tele. Zvážte nasledujúci príklad:

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

>

>

Tu kompilátor podľa nasledujúcich krokov vytvorí objekt typu MyClass

1. Konštruktor typu sa volá prvý pre a.

2. Predvolená premenná konštrukcie

3. Operátor priradenia Type sa volá v tele konštruktora MyClass() na priradenie

variable = a;>

4. A potom sa nakoniec zavolá deštruktor typu a, pretože ide mimo rozsah.

Teraz zvážte rovnaký kód s konštruktorom MyClass() so zoznamom inicializátorov

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

>

>

Pri zozname inicializátorov kompilátor nasleduje nasledujúce kroky:

1. Konštruktor typu sa volá prvý pre a.
2. Na inicializáciu sa volá parametrizovaný konštruktor triedy Type: variable(a). Argumenty v zozname inicializátora sa používajú na priame kopírovanie premennej konštrukcie.
3. Deštruktor typu sa volá a, pretože ide mimo rozsah.

Ako môžeme vidieť z tohto príkladu, ak použijeme priradenie v tele konštruktora, existujú tri volania funkcie: konštruktor + deštruktor + jedno volanie operátora priradenia sčítania. A ak použijeme zoznam inicializátorov, existujú iba dve volania funkcie: kopírovanie konštruktora + volanie deštruktora. Pozrite si tento príspevok, kde nájdete bežiaci príklad v tomto bode.

Táto penalizácia priradenia bude oveľa viac v reálnych aplikáciách, kde bude takýchto premenných veľa. Vďaka ptr za pridanie tohto bodu.

Parameter vs jednotná inicializácia v C++

Je lepšie použiť inicializačný zoznam s jednotnou inicializáciou {} namiesto inicializácie parametrov (), aby ste sa vyhli problémom so zúžením konverzií a neočakávaným správaním. Poskytuje prísnejšiu kontrolu typu počas inicializácie a zabraňuje potenciálnemu zúženiu konverzií

Kód pomocou inicializácie parametra ()

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

>

konštruktor v jave

>

Výkon

44>

Vo vyššie uvedenom kóde je hodnota 300 mimo platného rozsahu pre znak, čo môže viesť k nedefinovanému správaniu a potenciálne nesprávnym výsledkom. Kompilátor môže pre túto situáciu vygenerovať varovanie alebo chybu v závislosti od nastavení kompilácie.

Kód používajúci jednotnú inicializáciu {}

Použitím jednotnej inicializácie s {} a inicializáciou x s ​​poskytnutou hodnotou a, kompilátor vykoná prísnejšiu kontrolu typu a vydá varovanie alebo chybu počas kompilácie, čo naznačuje zúženie konverzie z int na char.
Tu je kód s jednotnou inicializáciou {} , čo má za následok varovanie a teda lepšie použitie

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