Sdílet prostřednictvím


Inicializátory

Inicializátor určuje počáteční hodnotu proměnné. Můžete inicializovat proměnné v těchto kontextech:

  • V definici proměnné:

    int i = 3;
    Point p1{ 1, 2 };
    
  • Jako jeden z parametrů funkce:

    set_point(Point{ 5, 6 });
    
  • Jako návratovou hodnotu funkce:

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

Inicializátory mohou mít tyto formy:

  • Výraz (nebo seznam výrazů oddělených čárkami) v závorkách:

    Point p1(1, 2);
    
  • Znaménko rovná se za výrazem:

    string s = "hello";
    
  • Seznam inicializátorů v závorkách. Seznam může být prázdný nebo se může skládat ze sady seznamů, jako v následujícím příkladu:

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

Druhy inicializace

Existuje několik druhů inicializace, které mohou nastat v různých fázích provádění programu. Různé druhy inicializace se vzájemně nevylučují – například inicializace seznamu může aktivovat inicializaci hodnoty a za jiných okolností může aktivovat agregovanou inicializaci.

Inicializace nuly

Inicializace nula je nastavení proměnné na hodnotu nula implicitně převedenou na typ:

  • Číselné proměnné jsou inicializovány na hodnotu 0 (nebo 0,0 nebo 0,0000000000 atd.).

  • Proměnné znaku jsou inicializovány na '\0'.

  • Ukazatele jsou inicializovány na nullptr.

  • Pole, třídy POD , struktury a sjednocení mají své členy inicializované na nulovou hodnotu.

Inicializace nula je provedena v různých časech:

  • Při spuštění programu pro všechny pojmenované proměnné, které mají statické trvání. Tyto proměnné mohou být později znovu inicializovány.

  • Během inicializace hodnoty pro skalární typy a typy tříd POD, které jsou inicializovány pomocí prázdné závorky.

  • Pro pole, která mají inicializovánu pouze podmnožinu svých členů.

Zde jsou některé příklady inicializace nula:

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

Výchozí inicializace

Výchozí inicializace tříd, struktur a sjednocení je inicializace s výchozím konstruktorem. Výchozí konstruktor lze volat bez inicializačního výrazu nebo s klíčovým slovem new :

MyClass mc1;
MyClass* mc3 = new MyClass;

Pokud třída, struktura nebo sjednocení nemají výchozí konstruktor, kompilátor vygeneruje chybu.

Skalární proměnné jsou ve výchozím nastavení inicializovány, když jsou definovány bez inicializačního výrazu. Mají neurčité hodnoty.

int i1;
float f;
char c;

Pole jsou ve výchozím nastavení inicializována, když jsou definována bez inicializačního výrazu. Pokud je pole inicializováno jako výchozí, jeho členy jsou výchozí inicializovány a mají neurčité hodnoty, jako v následujícím příkladu:

int int_arr[3];

Pokud členové pole nemají výchozí konstruktor, kompilátor vygeneruje chybu.

Výchozí inicializace konstantních proměnných

Konstantní proměnné musí být deklarovány společně s inicializátorem. Pokud se jedná o skalární typy, způsobí chybu kompilátoru a pokud se jedná o typy tříd, které mají výchozí konstruktor, způsobí upozornění:

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
}

Výchozí inicializace statických proměnných

Statické proměnné deklarované bez inicializátoru se inicializují na hodnotu 0 (implicitně se převedou na typ).

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

Další informace o inicializaci globálních statických objektů naleznete v tématu hlavní funkce a argumenty příkazového řádku.

Inicializace hodnot

Inicializace hodnoty se vyskytuje v následujících případech:

  • Pojmenovaná hodnota se inicializuje pomocí inicializace prázdných závorek.

  • Anonymní dočasný objekt se inicializuje pomocí prázdných závorek nebo závorek.

  • Objekt je inicializován pomocí klíčového new slova plus prázdné závorky nebo složené závorky.

Inicializace hodnoty provede následující:

  • pro třídy s alespoň jedním veřejným konstruktorem se volá výchozí konstruktor.

  • pro nonunion třídy bez deklarovaných konstruktorů, objekt je nula inicializován a výchozí konstruktor je volán

  • pro pole je každý prvek inicializován hodnotou.

  • ve všech ostatních případech je proměnná inicializována nula.

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
}

Inicializace kopírování

Inicializace kopírování je inicializace jednoho objektu pomocí jiného objektu. Dochází k němu v následujících případech:

  • Proměnná se inicializuje pomocí znaménka rovná se

  • Do funkce se předá argument.

  • Objekt je vrácen z funkce.

  • vyvolána nebo zachycena výjimka

  • Nestatický datový člen je inicializován pomocí znaménka rovná se

  • Třídy, struktury a sjednocující členy jsou inicializovány inicializací kopírování během agregované inicializace. Příklady najdete v části Agregace inicializace .

Následující kód ukazuje několik příkladů inicializace kopírování:

#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();
    }
}

Inicializace kopírování nemůže vyvolat explicitní konstruktory.

vector<int> v = 10; // the constructor is explicit; compiler error C2440: can't 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

V některých případech, pokud je kopírovací konstruktu třídy odstraněn nebo nepřístupný, kopírování inicializace způsobí chybu kompilátoru.

Přímá inicializace

Přímá inicializace se inicializuje pomocí (neprázdných) závorek nebo závorek. Na rozdíl od kopírování inicializace nemůže vyvolat explicitní konstruktory. Dochází k němu v následujících případech:

  • Proměnná se inicializuje s neprázdnými složenými závorkami nebo závorkami.

  • Proměnná se inicializuje pomocí klíčového new slova plus neprázdné složené závorky nebo závorky.

  • Proměnná se inicializuje pomocí static_cast

  • v konstruktoru se inicializují základní třídy a nestatické členy se seznamem inicializátorů.

  • v kopii zachycené proměnné uvnitř výrazu lambda

Následující kód ukazuje několik příkladů přímé inicializace:

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

Inicializace seznamu

Inicializace seznamu nastane, když je proměnná inicializována pomocí seznamu složených inicializátorů. Seznamy inicializátorů složených závorek lze použít v následujících případech:

  • Inicializuje se proměnná.

  • Třída je inicializována pomocí klíčového new slova

  • Objekt je vrácen z funkce.

  • argument předaný funkci

  • jeden z argumentů v přímé inicializaci

  • inicializátoru nestatického datového členu

  • v seznamu inicializátoru konstruktoru

Následující kód ukazuje několik příkladů inicializace seznamu:

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

Inicializace agregace

Inicializace agregace je forma inicializace seznamu pro pole nebo typy tříd (často struktury nebo sjednocení), které mají:

  • žádní soukromí nebo chránění členové

  • žádné konstruktory poskytované uživatelem s výjimkou explicitně výchozích nebo odstraněných konstruktorů

  • žádné základní třídy

  • žádné virtuální členské funkce

Poznámka:

V sadě Visual Studio 2015 a starších verzích nesmí agregace obsahovat inicializátory se složenou závorkou nebo rovnou inicializátorům pro nestatické členy. Toto omezení bylo odebráno ve standardu C++14 a implementováno v sadě Visual Studio 2017.

Agregační inicializátory se skládají ze seznamu závorek inicializace se znaménkem rovná se nebo bez, jak je znázorněno v následujícím příkladu:

#include <iostream>
using namespace std;

struct MyAggregate{
    int myInt;
    char myChar;
};

struct MyAggregate2{
    int myInt;
    char myChar = 'Z'; // member-initializer OK in C++14
};

int main() {
    MyAggregate agg1{ 1, 'c' };
    MyAggregate2 agg2{2};
    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;
}

Měl by se zobrazit následující výstup:

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

Důležité

Členy pole, které jsou deklarovány, ale nejsou explicitně inicializovány během agregační inicializace, jsou inicializovány nulou, jak je uvedeno výše myArr3 .

Inicializace sjednocení a struktur

Pokud sjednocení neobsahuje konstruktor, můžete ho inicializovat jednou hodnotou (nebo s jinou instancí sjednocení). Hodnota slouží k inicializaci prvního nestatické pole. Tím se liší od inicializace struktury, ve které první hodnota v inicializátoru slouží k inicializaci prvního pole, druhá k inicializaci druhé pole a tak dále. Porovnejte inicializaci sjednocení a struktur v následujícím příkladu:

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

Inicializace agregací obsahujících agregace

Agregační typy mohou obsahovat jiné agregační typy, například pole polí, pole struktur atd. Tyto typy se inicializují pomocí vnořených sad složených závorek, například:

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

Inicializace odkazů

Proměnné typu odkazu je nutné inicializovat objektem typu, ze kterého je typ odkazu odvozen, nebo objektem typu, jenž lze převést na typ, ze kterého je odvozen typ odkazu. Příklad:

// initializing_references.cpp
int iVar;
long lVar;
int main()
{
    long& LongRef1 = lVar;        // No conversion required.
    long& LongRef2 = iVar;        // Error C2440
    const long& LongRef3 = iVar;  // OK
    LongRef1 = 23L;               // Change lVar through a reference.
    LongRef2 = 11L;               // Change iVar through a reference.
    LongRef3 = 11L;               // Error C3892
}

Jediný způsob, jak inicializovat odkaz na dočasný objekt, je inicializovat objekt dočasné konstanty. Po inicializaci odkazuje proměnná typu odkazu vždy na stejný objekt; nejde změnit tak, aby odkazovaly na jiný objekt.

Přestože je syntaxe stejná, inicializace proměnných typu odkazu a přiřazení proměnné referenčního typu jsou sémanticky rozdílné. V předchozím příkladu se přiřazení, která mění iVar a lVar, jeví inicializacím totožně, ale mají různé účinky. Inicializace určuje objekt, na který odkazuje proměnná typu odkazu. Přiřazení se přiřadí prostřednictvím odkazu na odkazovaný objekt.

Protože předání argumentu typu odkazu na funkci a návratová hodnota typu odkazu funkce inicializace, jsou formální argumenty funkce inicializovány správně, stejně jako vrácené odkazy.

Proměnné typu odkazu mohou být deklarovány bez použití inicializátorů pouze v následujících případech:

  • Deklarace funkce (prototypy). Příklad:

    int func( int& );
    
  • Deklarace návratového typu funkce. Příklad:

    int& func( int& );
    
  • Deklarace členu třídy typu odkazu. Příklad:

    class c {public:   int& i;};
    
  • Deklarace proměnné explicitně určená jako extern. Příklad:

    extern int& iVal;
    

Při inicializaci proměnné typu odkaz používá kompilátor rozhodovací graf zobrazený na následujícím obrázku k výběru mezi vytvořením odkazu na objekt nebo vytvořením dočasného objektu, na který odkazuje:

Decision graph for initialization of reference types.

Rozhodovací graf začíná na: je inicializátor lvalue stejného typu nebo typ odvozený od typu odkazu? Pokud ano, odkaz odkazuje na objekt zadaný v inicializátoru. Pokud ne, další rozhodnutí je, zda je proměnná typu odkaz typu const inicializována a lze inicializátor implicitně převést na T? Pokud ano, vytvoří se dočasná proměnná a odkazová proměnná se stane názvem pro tento dočasný. Pokud ne, jedná se o chybu.

Rozhodovací graf pro inicializaci referenčních typů

Odkazy na volatile typy (deklarované jakovolatile typename&identifier) lze inicializovat s volatile objekty stejného typu nebo s objekty, které nebyly deklarovány jako .volatile Nelze je však inicializovat pomocí const objektů tohoto typu. Podobně lze odkazy na const typy (deklarované jako constnázev&identifikátoru) inicializovat pomocí const objektů stejného typu (nebo čehokoli, co má převod na tento typ nebo objekty, které nebyly deklarovány jako ).const Nelze je však inicializovat pomocí volatile objektů tohoto typu.

Odkazy, které nejsou kvalifikované buď pomocí klíčového const slova nebo volatile nebo mohou být inicializovány pouze s objekty deklarovány jako ani constvolatile.

Inicializace externích proměnných

Deklarace automatických, statických a externích proměnných mohou obsahovat inicializátory. Deklarace externích proměnných však mohou obsahovat inicializátory pouze v případě, že proměnné nejsou deklarovány jako extern.