Inicjatory
Inicjator określa wartość początkową zmiennej.Można zainicjować zmienne w tych kontekstach:
W definicji zmiennej:
int i = 3; Point p1{ 1, 2 };
Jako jeden z parametrów funkcji:
set_point(Point{ 5, 6 });
Jako wartość zwracana funkcji:
Point get_new_point(int x, int y) { return { x, y }; } Point get_new_point(int x, int y) { return Point{ x, y }; }
Inicjatory mogą przybierać następujące formy:
Wyrażenie (lub rozdzielana przecinkami lista wyrażeń) w nawiasach:
Point p1(1, 2);
Znak równości i następujące po nim wyrażenie:
string s = "hello";
Lista inicjatorów w nawiasach klamrowych.Lista może być pusta lub może się składać z zestawu list:
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 } }); }
Rodzaje inicjowania
Istnieje kilka rodzajów inicjowania, które mogą wystąpić w różnych punktach wykonywania programu.Różne rodzaje inicjowania nie wykluczają się nawzajem — na przykład inicjowanie listy może wywołać inicjowanie wartości, a w innych okolicznościach może wywołać inicjowanie agregacji.
Inicjalizacja wartością zerową.
Inicjalizacja wartością zerową to ustawienie zmiennej na wartość zero niejawnie konwertowaną na typ:
Zmienne liczbowe są inicjowane na wartość 0 (lub 0,0 lub 0,0000000000 itp.).
Zmienne char są inicjowane na ‘\0’.
Wskaźniki są inicjowane na nullptr.
Elementy członkowskie tablic, klas POD, struktur i unii są inicjowane z wartością zero.
Inicjalizacja z wartością zerową odbywa się w różnym czasie:
Podczas uruchamiania programu, dla wszystkich zmiennych nazwanych, które mają statyczny czas trwania.Te zmienne mogą być później ponownie zainicjowane.
Podczas inicjowania wartości, dla typów skalarnych i typów klasy POD, które są inicjowane za pomocą pustych nawiasów klamrowych.
Dla tablic, które mają zainicjowany tylko podzbiór swoich elementów członkowskich.
Oto kilka przykładów inicjalizacji z wartością zerową:
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'
}
Inicjowanie domyślne
Inicjowanie domyślne klas, struktur i unii to inicjowanie, które używa domyślnego konstruktora.Konstruktor domyślny może być wywoływany przez nieumieszczenie wyrażenia inicjowania lub za pomocą słowa kluczowego new:
MyClass mc1;
MyClass* mc3 = new MyClass;
Jeśli klasa, struktura lub unia nie ma domyślnego konstruktora, kompilator generuje błąd.
Zmienne skalarne są inicjowane domyślnie, gdy są zdefiniowane bez wyrażenia inicjowania.Mają one wartości nieokreślone.
int i1;
float f;
char c;
Tablice są inicjowane domyślnie, gdy są zdefiniowane bez wyrażenia inicjowania.Gdy tablica jest zainicjowana domyślnie, jej elementy członkowskie są inicjowane domyślnie i mają wartości nieokreślone:
int int_arr[3];
Jeśli elementy członkowskie tablicy nie mają domyślnego konstruktora, kompilator generuje błąd.
Domyślna inicjalizacja stałych zmiennych
Stałe zmienne muszą zostać zadeklarowane wraz z inicjatorem.Jeśli są one typu skalarnego, powodują błąd kompilatora, a jeśli są typami klas, które mają domyślny konstruktor, powodują one ostrzeżenie:
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
}
Domyślna inicjalizacja zmiennych statycznych
Zmienne statyczne, które są zadeklarowane bez inicjatorów, są inicjowane na wartość 0 (niejawnie konwertowane 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'}
}
Aby uzyskać więcej informacji na temat inicjowania statycznych obiektów globalnych, zobacz Dodatkowe zagadnienia dotyczące uruchamiania.
Inicjalizacja wartości.
Inicjalizacja wartości występuje w następujących przypadkach:
Wartość nazwana jest inicjowana za pomocą pustych nawiasów klamrowych.
Anonimowy obiekt tymczasowy jest inicjowany z użyciem pustych nawiasów zwykłych lub klamrowych.
Obiekt jest inicjowany z użyciem słowa kluczowego new i pustych nawiasów zwykłych lub klamrowych.
Inicjowanie wartości ma następujące efekty:
Dla klas, które mają co najmniej jeden konstruktor publiczny, wywoływany jest konstruktor domyślny.
Dla klas spoza unii, które nie mają deklarowanych konstruktorów, obiekt jest inicjowany z wartością zerową i wywoływany jest konstruktor domyślny.
Dla tablic, każdy element ma zainicjowaną wartość.
We wszystkich innych przypadkach zmienna jest zainicjowana z wartością zerową.
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
}
Inicjalizacja kopiująca.
Inicjalizacja kopiująca to inicjalizacja jednego obiektu przy użyciu innego obiektu.Występuje ona w następujących przypadkach:
Zmienna jest inicjowana przy użyciu znaku równości.
Argument jest przekazywany do funkcji.
Obiekt jest zwracany przez funkcję.
Wyjątek jest zgłaszany lub przechwytywany.
Niestatyczny element członkowski danych jest inicjowany za pomocą znaku równości.
Elementy członkowskie klas, struktur i unii są inicjowane przez inicjalizację kopiującą podczas inicjowania agregacji.Zobacz przykłady w sekcji Inicjalizacja agregacji.
Ten kod zawiera przykłady inicjalizacji kopiującej:
#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();
}
}
Inicjalizacja kopiująca nie może wywołać jawnych konstruktorów:
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
W niektórych przypadkach, jeśli konstruktor kopiujący klasy jest usunięty lub niedostępny, inicjalizacja kopiująca powoduje błąd kompilatora.Aby uzyskać więcej informacji, zobacz Jawne inicjowanie.
Inicjalizacja bezpośrednia
Inicjalizacja bezpośrednia używa (niepustych) nawiasów zwykłych lub klamrowych.W przeciwieństwie do inicjalizacji kopiującej, może wywołać jawne konstruktory.Występuje ona w następujących przypadkach:
Zmienna jest inicjowana za pomocą niepustych nawiasów zwykłych lub klamrowych.
Zmienna jest inicjowana z użyciem słowa kluczowego new i niepustych nawiasów zwykłych lub klamrowych.
Zmienna jest inicjowana przy użyciu static_cast.
W konstruktorze, klasy podstawowe i niestatyczne elementy członkowskie są inicjowane za pomocą list inicjatora.
W kopii przechwyconych zmiennej w wyrażeniu lambda.
Oto kilka przykładów inicjalizacji bezpośredniej:
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();
}
Inicjalizacja listy.
Inicjalizacja listy występuje, gdy zmienna jest ustawiana za pomocą listy inicjatora w nawiasach klamrowych.Listy inicjatora w nawiasach klamrowych mogą być używane w następujących przypadkach:
Zmienna jest inicjowana.
Klasa jest inicjowana za pomocą słowa kluczowego new.
Obiekt jest zwracany przez funkcję.
Argument jest przekazywany do funkcji.
Jeden z argumentów w inicjalizacji bezpośredniej.
W inicjatorze niestatycznych elementów członkowskich danych
Na liście inicjatora konstruktora.
Przykłady inicjalizacji listy:
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' } };
}
Inicjalizacja agregacji
Inicjalizacja agregacji jest formą inicjalizacji listy dla tablic lub typów klas (zwykle struktur lub unii), które:
Nie mają elementów członkowskich prywatnych ani chronionych.
Nie mają dostarczonych przez użytkownika konstruktorów, z wyjątkiem jawnie ustawionych domyślnie lub usuniętych konstruktorów.
Nie mają klas podstawowych.
Nie mają funkcji wirtualnych elementów członkowskich.
Nie mają inicjatorów w postaci nawiasów klamrowych lub znaku równości dla niestatycznych elementów członkowskich.
Inicjatory agregacji składają się z listy inicjowania w nawiasach klamrowych, ze znakiem równości lub bez niego:
#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;
}
Oto dane wyjściowe:
agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0
![]() |
---|
Elementy członkowskie tablicy zadeklarowane, ale niezainicjowane podczas inicjalizacji agregacji są inicjowane z wartością zerową, jak w myArr3. |
Inicjowanie unii i struktur
Jeżeli unia nie ma konstruktora, można ją zainicjować przy użyciu wartości (lub za pomocą innego wystąpienia unii).Wartość służy do inicjowania pierwszego pola niestatycznego.Różni się to od inicjowania struktury, w którym pierwsza wartość w inicjatorze służy do inicjowania pierwszego pola, druga do zainicjowania drugiego pola itd.Porównaj inicjowania unii i struktur w tym przykładzie:
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'
}
Inicjowanie agregacji, które zawierają agregacje
Typy agregacji mogą zawierać inne typy agregacji — na przykład, tablice tablic, tablice struktur itd.Te typy są inicjowane przy użyciu zagnieżdżonych zestawów nawiasów klamrowych:
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'} };
}
Inicjalizacja odwołania
Aby uzyskać informacje o inicjalizacji odwołania, zobacz Inicjowanie odwołań.
Inicjalizacja zmiennych zewnętrznych
Deklaracje zmiennych automatycznych, rejestrowych, statycznych i zewnętrznych mogą zawierać inicjatory.Deklaracje zmiennych zewnętrznych mogą jednak zawierać inicjatory tylko, jeśli zmienne nie są deklarowane jako extern.Aby uzyskać więcej informacji, zobacz Zewnętrzne.