logo

Quando utilizziamo l'elenco degli inizializzatori in C++?

L'elenco degli inizializzatori viene utilizzato per inizializzare i membri dati di una classe. L'elenco dei membri da inizializzare è indicato con il costruttore come un elenco separato da virgole seguito da due punti. Di seguito è riportato un esempio che utilizza l'elenco di inizializzatori per inizializzare xey della classe Point.

Esempio



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



>

>

Produzione

x = 10, y = 15>

Il codice precedente è solo un esempio di sintassi dell'elenco Inizializzatore. Nel codice precedente, xey possono anche essere facilmente inizializzati all'interno del costruttore. Ma ci sono situazioni in cui l'inizializzazione dei membri dati all'interno del costruttore non funziona ed è necessario utilizzare l'elenco degli inizializzatori. I seguenti sono casi simili:

1. Per l'inizializzazione di membri dati const non statici

const i membri dati devono essere inizializzati utilizzando l'elenco degli inizializzatori. Nell'esempio seguente, t è un membro dati const della classe Test e viene inizializzato utilizzando l'elenco degli inizializzatori. Il motivo per inizializzare il membro dati const nell'elenco degli inizializzatori è perché nessuna memoria è allocata separatamente per il membro dati const, è ripiegata nella tabella dei simboli per cui dobbiamo inizializzarla nell'elenco degli inizializzatori.

Inoltre, è un costruttore parametrizzato e non è necessario chiamare l'operatore di assegnazione, il che significa che stiamo evitando un'operazione aggiuntiva.

Esempio

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

>

>

Produzione

10>

2. Per l'inizializzazione dei membri di riferimento

I membri di riferimento devono essere inizializzati utilizzando l'elenco di inizializzatori. Nell'esempio seguente t è un membro di riferimento della classe Test e viene inizializzato utilizzando l'elenco degli inizializzatori.

Esempio

C++


array nei metodi 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; }>

>

>

Produzione

20 30>

3. Per l'inizializzazione di oggetti membro che non dispongono di un costruttore predefinito

Nell'esempio seguente, un oggetto a della classe A è un membro dati della classe B e A non ha un costruttore predefinito. L'elenco degli inizializzatori deve essere utilizzato per inizializzare a.

Esempio

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

>

>

Produzione

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

Se la classe A avesse sia costruttori predefiniti che parametrizzati, allora l'Elenco inizializzatori non è un must se vogliamo inizializzare a utilizzando il costruttore predefinito, ma è necessario inizializzare a utilizzando il costruttore parametrizzato.

4. Per l'inizializzazione dei membri della classe base

Come al punto 3, il costruttore parametrizzato della classe base può essere chiamato solo utilizzando l'elenco degli inizializzatori.

Esempio

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

>

>

Produzione

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

5. Quando il nome del parametro del costruttore è uguale al membro dati

Se il nome del parametro del costruttore è uguale al nome del membro dati, il membro dati deve essere inizializzato utilizzando questo puntatore o Elenco inizializzatori. Nell'esempio seguente, sia il nome del membro che il nome del parametro per A() sono i.

Esempio

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

>

>

Produzione

10 di 60
10>

6. Per motivi di prestazione

È preferibile inizializzare tutte le variabili di classe nell'elenco degli inizializzatori invece di assegnare valori all'interno del corpo. Considera il seguente esempio:

Esempio

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

>

>

Qui il compilatore segue i seguenti passaggi per creare un oggetto di tipo MyClass

1. Il costruttore del tipo viene chiamato prima per a.

2. Variabile di costrutto predefinita

3. L'operatore di assegnazione di Type viene richiamato all'interno del corpo del costruttore MyClass() da assegnare

variable = a;>

4. E infine viene chiamato il distruttore di Type poiché esce dall'ambito.

Consideriamo ora lo stesso codice con il costruttore MyClass() con l'elenco degli inizializzatori

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

>

>

Con l'elenco degli inizializzatori, il compilatore segue i seguenti passaggi:

1. Il costruttore del tipo viene chiamato prima per a.
2. Il costruttore parametrizzato della classe Type viene chiamato per inizializzare: variabile(a). Gli argomenti nell'elenco degli inizializzatori vengono utilizzati per copiare direttamente la variabile costrutta.
3. Viene richiamato il distruttore di Type poiché esce dall'ambito.

Come possiamo vedere da questo esempio, se utilizziamo l'assegnazione all'interno del corpo del costruttore ci sono tre chiamate di funzione: costruttore + distruttore + una chiamata dell'operatore di assegnazione di addizione. E se utilizziamo l'elenco degli inizializzatori ci sono solo due chiamate di funzione: copia chiamata del costruttore + chiamata del distruttore. Vedi questo post per un esempio in esecuzione su questo punto.

Questa penalità di assegnazione sarà molto maggiore nelle applicazioni reali dove saranno presenti molte variabili di questo tipo. Grazie a ptr per aver aggiunto questo punto.

Parametri e inizializzazione uniforme in C++

È preferibile utilizzare un elenco di inizializzazione con inizializzazione uniforme {} anziché l'inizializzazione dei parametri () per evitare il problema di conversioni restrittive e comportamenti imprevisti. Fornisce un controllo del tipo più rigoroso durante l'inizializzazione e impedisce potenziali conversioni restrittive

Codice utilizzando l'inizializzazione dei parametri ()

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

>

>

Produzione

44>

Nel codice precedente, il valore 300 non è compreso nell'intervallo valido per char, il che potrebbe portare a un comportamento indefinito e a risultati potenzialmente errati. Il compilatore potrebbe generare un avviso o un errore per questa situazione, a seconda delle impostazioni di compilazione.

Codice che utilizza l'inizializzazione uniforme {}

Utilizzando l'inizializzazione uniforme con {} e inizializzando x con il valore fornito a, il compilatore eseguirà un controllo del tipo più rigoroso e genererà un avviso o un errore durante la compilazione, indicando la conversione restrittiva da int a char.
Ecco il codice con inizializzazione uniforme {} , che genera un avviso e quindi è meglio utilizzarlo

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