Quali sono le differenze tra l’inizializzazione simile a C, il costruttore e l’uniforms?

TTBOMK, Ci sono tre modi per inizializzare una variabile in C ++.

int x = 0; // C-like initialization int x (0); // Constructor initialization int x {0}; // Uniform initialization 

L’inizializzazione uniforms è stata introdotta per C ++ 11 per fornire una syntax più uniforms per l’inizializzazione di diversi tipi di variabili, che richiedeva una syntax diversa in C ++ 03 .

Quali sono le differenze tra l’inizializzazione simile a C, il costruttore e l’uniforms? E dovrei sempre usare l’inizializzazione uniforms?

Innanzitutto, consiglierei di guardare il seguente discorso di Herb Sutter, in cui fornisce alcune raccomandazioni sull’argomento. La discussione sull’inizializzazione delle coppie inizia intorno alle 23:00 .

Quando parli di tipi di dati primitivi, tutti e 3 producono lo stesso risultato. Personalmente preferisco attenermi alla vecchia syntax int x = 0 , ma si tratta di preferenze personali.

Per i tipi di class, l’inizializzazione delle parentesi e l’inizializzazione del costruttore old-school non sono completamente intercambiabili. Per esempio:

 vector v (100); // Creates a 100-element vector vector v {100}; // Creates a 1-element vector, holding the value 100. 

Questo perché std::vector ha un costruttore che definisce in modo esplicito std::initializer_list come unico argomento. Tieni presente che

 auto var = {1, 2}; 

crea una std::initializer_list , con var come identificatore.

La cosa sugli elenchi di inizializzatori è che forniscono coerenza che è un cambiamento gradito da ciò che era disponibile in anticipo. Ad esempio, se dovessi inizializzare un array in C ++, dovresti usare:

 int arr[] = {1, 2, 3, 4}; 

Ma, se volessi inizializzare un vector con gli stessi elementi, dovevi:

  1. Inizializza prima l’arr primo e poi passa arr e arr + 4
  2. Crea il vettore e push_back () gli elementi singolarmente o in un ciclo.

Con C ++ 11, puoi semplicemente usarlo

 vector v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional 

Un’altra istanza in cui l’inizializzazione del brace è utile è che fornisce una soluzione alternativa all’analisi più irritante di C ++. Dal discorso, supponiamo di avere due classi, origin ed extents , le cui istanze possono essere passate per build un altro object di tipo rectangle . La seguente dichiarazione:

 rectangle w(origin(), extents()); 

non ti permette di creare un object rectangle usando l’ origin ed extents provvisori, perché quella dichiarazione è analizzata come una dichiarazione di funzione. Tsk tsk. Quindi normalmente, dovresti fare:

 origin o; extents e; rectangle w(o, e); 

Con l’inizializzazione del controvento, è ansible crearli al volo e

 rectangle w {origin(), extents()}; 

funzionerà come previsto, vale a dire passato al costruttore che è sovraccarico di un object di origin come primo argomento e un object di extents come il secondo.

La regola è per gli oggetti, utilizzare l’inizializzazione controvento a meno che non si abbia una ragione per non farlo.

Quali sono le differenze tra l’inizializzazione c-like, constructor e uniforms?

Per i tipi primitivi come int , non c’è alcuna differenza pratica; quindi consideriamo un tipo di class T

Il primo stile è equivalente a

 T x(T(0)); 

creando un object temporaneo dall’espressione di inizializzazione e quindi inizializzando x spostandolo o copiandolo. In pratica, la mossa o copia sarà elisa, in modo che il risultato sia lo stesso del secondo stile; l’unica differenza è che il primo fallirà se non c’è una copia accessibile o un costruttore di mosse.

Il secondo inizializza direttamente l’object usando un costruttore che accetta un argomento, dando un errore se non c’è un costruttore adatto.

Il terzo dipende da quali costruttori sono disponibili.

  • se c’è un costruttore che prende std::initializer_list , lo usa;
  • altrimenti, se c’è un costruttore che prende un singolo argomento di un tipo adatto, lo usa;
  • altrimenti, se è un aggregato (senza costruttori) con un membro, quel membro viene inizializzato con zero;
  • altrimenti, è un errore.

E dovrei sempre usare l’inizializzazione uniforms?

No. A volte è necessario l’inizializzazione in stile funzione per distinguere tra un costruttore initializer_list e uno che assume altri tipi di argomenti. Per esempio:

 std::vector v1(10, 42); // 10 elements with value 42 std::vector v2{10, 42}; // 2 elements with values 10 and 42 

Inoltre, non dovresti chiamarla “inizializzazione uniforms” poiché non è “uniforms” in alcun senso significativo. Il termine ufficiale è “brace-initialisation”.