Condividi tramite


Inizializzatori

Un inizializzatore specifica il valore iniziale di una variabile. È possibile inizializzare le variabili nei contesti seguenti:

  • Nella definizione di una variabile:

    int i = 3;
    Point p1{ 1, 2 };
    
  • Come uno dei parametri di una funzione:

    set_point(Point{ 5, 6 });
    
  • Come valore restituito di una funzione:

    Point get_new_point(int x, int y) { return { x, y }; }
    Point get_new_point(int x, int y) { return Point{ x, y }; }
    

Gli inizializzatori possono assumere le forme seguenti:

  • Un'espressione (o un elenco di espressioni separato da virgole) tra parentesi:

    Point p1(1, 2);
    
  • Un segno di uguale seguito da un'espressione:

    string s = "hello";
    
  • Un elenco di inizializzatori tra parentesi graffe. L'elenco può essere vuoto o può essere costituito da un set di elenchi:

    struct Point{
        int x;
        int y;
    };
    class PointConsumer{
    public:
        void set_point(Point p){};
        void set_points(initializer_list<Point> my_list){};
    };
    int main() {
        PointConsumer pc{};
        pc.set_point({});
        pc.set_point({ 3, 4 });
        pc.set_points({ { 3, 4 }, { 5, 6 } });
    }
    

Tipi di inizializzazione

Sono disponibili diversi tipi di inizializzazione, che possono verificarsi in diversi momenti dell'esecuzione del programma. Tipi diversi di inizializzazione non si escludono reciprocamente, ad esempio l'inizializzazione elenco può attivare l'inizializzazione valore e in altre circostanze attivare l'inizializzazione aggregata.

Inizializzazione zero

L'inizializzazione zero è l'impostazione di una variabile su un valore zero convertito implicitamente nel tipo:

  • Le variabili numeriche vengono inizializzate su 0 o 0,0 oppure 0,0000000000 e così via.

  • Le variabili char vengono inizializzate su '\ 0'.

  • I puntatori vengono inizializzati su nullptr.

  • I membri di matrici, classi POD, struct e unioni vengono inizializzati sul valore zero.

L'inizializzazione zero viene eseguita in momenti diversi:

  • All'avvio del programma, per tutte le variabili denominate aventi una durata statica. Queste variabili possono essere reinizializzate in un momento successivo.

  • Durante l'inizializzazione valore, per i tipi scalari e tipi di classe POD inizializzati tramite parentesi graffe vuote.

  • Per le matrici che contengono un solo subset dei relativi membri inizializzati.

Di seguito sono riportati alcuni esempi di inizializzazione zero:

struct my_struct{
    int i;
    char c;
};

int i0;              // zero-initialized to 0
int main() {
    static float f1;  // zero-initialized to 0.000000000
    double d{};     // zero-initialized to 0.00000000000000000
    int* ptr{};     // initialized to nullptr
    char s_array[3]{'a', 'b'};  // the third char is initialized to '\0'
    int int_array[5] = { 8, 9, 10 };  // the fourth and fifth ints are initialized to 0
    my_struct a_struct{};   // i = 0, c = '\0'
}

Inizializzazione predefinita

L'inizializzazione predefinita per classi, struct e unioni è l'inizializzazione che utilizza un costruttore predefinito. Il costruttore predefinito può essere chiamato evitando di includere un'espressione di inizializzazione oppure utilizzando la parola chiave new:

MyClass mc1;
MyClass* mc3 = new MyClass;

Se la classe, lo struct o l'unione non dispone di un costruttore predefinito, viene generato un errore.

Le variabili scalari vengono inizializzate in base alla modalità predefinita quando vengono definite senza un'espressione di inizializzazione. Presentano valori indeterminati.

int i1;
float f;
char c;

Le matrici vengono inizializzate secondo la modalità predefinita quando vengono definite senza un'espressione di inizializzazione. Quando una matrice viene inizializzata secondo la modalità predefinita, i relativi membri vengono inizializzati secondo la modalità predefinita e presentano valori indeterminati:

int int_arr[3];

Se i membri della matrice non dispongono di un costruttore predefinito, viene generato un errore.

Inizializzazione predefinita di variabili costanti

Le variabili costanti devono essere dichiarate con un inizializzatore. Se sono tipi scalari, viene generato un errore del compilatore, se sono tipi di classe con un costruttore predefinito viene generato un avviso:

class MyClass{};
int main() {
    //const int i2;   // compiler error C2734: const object must be initialized if not extern
    //const char c2;  // same error
    const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}

Inizializzazione predefinita di variabili statiche

Le variabili statiche dichiarate senza un inizializzatore vengono inizializzate su 0 (convertito implicitamente nel tipo):

class MyClass {   
private:
    int m_int;
    char m_char;
};

int main() {
    static int int1;       // 0
    static char char1;     // '\0'
    static bool bool1;   // false
    static MyClass mc1;     // {0, '\0'}
}

Per ulteriori informazioni sull'inizializzazione di oggetti statici globali, vedere Considerazioni di avvio aggiuntive.

Inizializzazione valore

L'inizializzazione valore si verifica nei casi seguenti:

  • Un valore denominato viene inizializzato tramite l'inizializzazione con parentesi graffe vuote.

  • Un oggetto temporaneo anonimo viene inizializzato con parentesi o parentesi graffe vuote.

  • Un oggetto viene inizializzato tramite la parola chiave new più le parentesi o le parentesi graffe vuote.

L'inizializzazione valore prevede i comportamenti seguenti:

  • Per le classi con almeno un costruttore pubblico, viene chiamato il costruttore predefinito.

  • Per le classi non di unione che non dispongono di costruttori dichiarati, l'oggetto viene sottoposto a inizializzazione zero e viene chiamato il costruttore predefinito.

  • Per le matrici, ogni elemento viene sottoposto a inizializzazione valore.

  • In tutti gli altri casi, la variabile viene sottoposta a inizializzazione zero.

class BaseClass {  
private:
    int m_int;
};

int main() {
    BaseClass bc{};     // class is initialized
    BaseClass*  bc2 = new BaseClass();  // class is initialized, m_int value is 0
    int int_arr[3]{};  // value of all members is 0
    int a{};     // value of a is 0
    double b{};  // value of b is 0.00000000000000000
}

Inizializzazione per copia

L'inizializzazione per copia è l'inizializzazione di un oggetto utilizzando un oggetto diverso. Si verifica nei casi seguenti:

  • Una variabile viene inizializzata utilizzando un segno di uguale.

  • Un argomento è passato a una funzione.

  • Un oggetto viene restituito da una funzione.

  • Un'eccezione viene generata o intercettata.

  • Un membro dati non statico viene inizializzato con un segno di uguale.

  • I membri di classi, struct e unioni vengono inizializzati tramite l'inizializzazione per copia durante l'inizializzazione aggregata. Per alcuni esempi, vedere Inizializzazione aggregata.

In questo codice vengono illustrati alcuni esempi di inizializzazione per copia:

#include <iostream>
using namespace std;

class MyClass{
public:
    MyClass(int myInt) {}
    void set_int(int myInt) { m_int = myInt; }
    int get_int() const { return m_int; }
private:
    int m_int = 7; // copy initialization of m_int

};
class MyException : public exception{};
int main() {
    int i = 5;              // copy initialization of i
    MyClass mc1{ i };
    MyClass mc2 = mc1;      // copy initialization of mc2 from mc1
    MyClass mc1.set_int(i);    // copy initialization of parameter from i
    int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()

    try{
        throw MyException();    
    }
    catch (MyException ex){ // copy initialization of ex
        cout << ex.what();  
    }
}

L'inizializzazione per copia non può richiamare costruttori espliciti:

vector<int> v = 10; // the constructor is explicit; compiler error C2440: cannot convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error

In alcuni casi, se il costruttore di copia della classe viene eliminato o è inaccessibile, viene generato un errore del compilatore. Per ulteriori informazioni, vedere Inizializzazione esplicita.

Inizializzazione diretta

L'inizializzazione diretta utilizza parentesi o parentesi graffe non vuote. A differenza dell'inizializzazione per copia, può richiamare costruttori espliciti. Si verifica nei casi seguenti:

  • Una variabile viene inizializzata utilizzando parentesi o parentesi graffe non vuote.

  • Una variabile viene inizializzata utilizzando la parola chiave new più parentesi o parentesi graffe non vuote.

  • Una variabile viene inizializzata tramite static_cast.

  • In un costruttore le classi base e i membri non statici vengono inizializzati tramite un elenco di inizializzatori.

  • Nella copia di una variabile acquisita in un'espressione lambda.

Di seguito sono riportati alcuni esempi di inizializzazione diretta:

class BaseClass{
public:
    BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
    int m_int;
};

class DerivedClass : public BaseClass{
public:
    // BaseClass and m_char are direct initialized
    DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
    char m_char;
};
int main(){
    BaseClass bc1(5);
    DerivedClass dc1{ 1, 'c' };
    BaseClass* bc2 = new BaseClass(7);
    BaseClass bc3 = static_cast<BaseClass>(dc1);

    int a = 1;
    function<int()> func = [a](){  return a + 1; }; // a is direct initialized
    int n = func();
}

Inizializzazione elenco

L'inizializzazione elenco si verifica quando una variabile viene inizializzata tramite un elenco di inizializzatori racchiusi tra parentesi graffe. Gli elenchi di inizializzatori tra parentesi graffe possono essere utilizzati nei casi seguenti:

  • Una variabile viene inizializzata.

  • Una classe viene inizializzata utilizzando la parola chiave new.

  • Un oggetto viene restituito da una funzione.

  • Un argomento viene passato a una funzione.

  • Uno degli argomenti in un'inizializzazione diretta.

  • In un inizializzatore di un membro dati non statico.

  • In un elenco di inizializzatori del costruttore.

Esempi di inizializzazione elenco:

class MyClass {
public:
    MyClass(int myInt, char myChar) {}  
private:
    int m_int[]{ 3 };
    char m_char;
};
class MyClassConsumer{
public:
    void set_class(MyClass c) {}
    MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
    int my_int;
    char my_char;
    MyClass my_class;
};
int main() {
    MyClass mc1{ 1, 'a' };
    MyClass* mc2 = new MyClass{ 2, 'b' };
    MyClass mc3 = { 3, 'c' };

    MyClassConsumer mcc;
    mcc.set_class(MyClass{ 3, 'c' });
    mcc.set_class({ 4, 'd' });

    MyStruct ms1{ 1, 'a', { 2, 'b' } };
}

Inizializzazione aggregata

L'inizializzazione aggregata è un tipo di inizializzazione elenco per matrici o tipi di classe (spesso struct o unioni) che non dispongono di:

  • Alcun membro privato o protetto.

  • Alcun costruttore fornito dall'utente, ad eccezione dei costruttori esplicitamente impostati come predefiniti o eliminati.

  • Alcuna classe base.

  • Alcuna funzione membro virtuale.

  • Alcun inizializzatore a parentesi graffa o simbolo di uguale per membri non statici.

Gli inizializzatori di aggregazione sono costituiti da un elenco di inizializzazione tra parentesi graffe, con o senza un segno di uguale:

#include <iostream>
using namespace std;

struct MyAggregate{
    int myInt;
    char myChar;
};

int main() {
    MyAggregate agg1{ 1, 'c' };

    cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
    cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;

    int myArr1[]{ 1, 2, 3, 4 };
    int myArr2[3] = { 5, 6, 7 };
    int myArr3[5] = { 8, 9, 10 };

    cout << "myArr1: ";
    for (int i : myArr1){
        cout << i << " ";
    }
    cout << endl;
    
    cout << "myArr3: ";
    for (auto const &i : myArr3) {
        cout << i << " ";
    }
    cout << endl;
}

Di seguito viene riportato l'output:

agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0

Importante

I membri di matrici dichiarati, ma non inizializzati in modo esplicito durante l'inizializzazione aggregata vengono sottoposti a inizializzazione zero, come in myArr3.

Inizializzare di unioni e struct

Se un'unione non dispone di costruttore, è possibile inizializzarla utilizzando un valore (o un'altra istanza di un'unione). Il valore viene utilizzato per inizializzare il primo campo non statico. Questo non accade con l'inizializzazione struct in quanto il primo valore dell'inizializzatore viene utilizzato per inizializzare il primo campo, il secondo per inizializzare il secondo campo e così via... Confrontare l'inizializzazione di unioni e struct in questo esempio:

struct MyStruct {
    int myInt;
    char myChar;
};
union MyUnion {
    int my_int;
    char my_char;
    bool my_bool;
    MyStruct my_struct;
};

int main() {  
    MyUnion mu1{ 'a' };  // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
    MyUnion mu2{ 1 };   // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
    MyUnion mu3{};      // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    MyUnion mu4 = mu3;  // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    //MyUnion mu5{ 1, 'a', true };  // compiler error: C2078: too many initializers
    //MyUnion mu6 = 'a';            // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
    //MyUnion mu7 = 1;              // compiler error: C2440: cannot convert from 'int' to 'MyUnion'

    MyStruct ms1{ 'a' };            // myInt = 97, myChar = '\0'
    MyStruct ms2{ 1 };              // myInt = 1, myChar = '\0'
    MyStruct ms3{};                 // myInt = 0, myChar = '\0'
    MyStruct ms4{1, 'a'};           // myInt = 1, myChar = 'a'
    MyStruct ms5 = { 2, 'b' };      // myInt = 2, myChar = 'b'
}

Inizializzazione di aggregati che contengono aggregati

I tipi di aggregati possono contenere altri tipi di aggregati, ad esempio matrici di matrici, matrici di struct e così via. Questi tipi vengono inizializzati utilizzando set annidati di parentesi graffe:

struct MyStruct {
    int myInt;
    char myChar;
};
int main() {
    int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
    int intArr3[2][2] = {1, 2, 3, 4};  
    MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}

Inizializzazione di riferimento

Per informazioni sull'inizializzazione di riferimento, vedere Inizializzazione di riferimenti.

Inizializzazione di variabili esterne

Dichiarazioni di variabili esterne, automatiche, di registro e statiche possono contenere inizializzatori. Tuttavia, le dichiarazioni di variabili esterne possono contenere inizializzatori solo se le variabili non vengono dichiarate come extern. Per ulteriori informazioni, vedere Elementi esterni.

Vedere anche

Riferimenti

Dichiaratori