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 składać się z zestawu list, jak w poniższym przykładzie:
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 inicjalizacji
Istnieje kilka rodzajów inicjowania, które mogą wystąpić w różnych punktach wykonywania programu. Różne rodzaje inicjowania nie wykluczają się wzajemnie — na przykład inicjowanie listy może wyzwalać inicjowanie wartości, a w innych okolicznościach może wyzwolić inicjowanie agregacji.
Inicjowanie zerowe
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 do
nullptr
.Tablice, klasy POD , struktury i związki mają swoje składowe zainicjowane do wartości zerowej.
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
Domyślna inicjalizacja klas, struktur i związków to inicjowanie za pomocą konstruktora domyślnego. Konstruktor domyślny może być wywoływany bez wyrażenia inicjalizacji lub słowa kluczowego new
:
MyClass mc1;
MyClass* mc3 = new MyClass;
Jeśli klasa, struktura lub unia nie ma konstruktora domyślnego, kompilator emituje błąd.
Zmienne skalarne są domyślnie inicjowane, gdy są definiowane bez wyrażenia inicjalizacji. Mają one wartości nieokreślone.
int i1;
float f;
char c;
Tablice są domyślnie inicjowane, gdy są zdefiniowane bez wyrażenia inicjalizacji. Gdy tablica jest inicjowana domyślnie, jej składowe są inicjowane domyślnie i mają nieokreślone wartości, jak w poniższym przykładzie:
int int_arr[3];
Jeśli elementy członkowskie tablicy nie mają konstruktora domyślnego, kompilator emituje błąd.
Domyślna inicjowanie zmiennych stałych
Stałe zmienne muszą zostać zadeklarowane wraz z inicjatorem. Jeśli są typami skalarnym, powodują błąd kompilatora, a jeśli są typami klas, które mają domyślny konstruktor, powodują 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 inicjowanie zmiennych statycznych
Zmienne statyczne zadeklarowane bez inicjatora 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 globalnych obiektów statycznych, zobacz główne funkcje i argumenty wiersza polecenia.
Inicjowanie wartości
Inicjowanie wartości odbywa się w następujących przypadkach:
nazwana wartość jest inicjowana przy użyciu inicjowania pustego nawiasu klamrowego
anonimowy obiekt tymczasowy jest inicjowany przy użyciu pustych nawiasów lub nawiasów klamrowych
obiekt jest inicjowany za pomocą słowa kluczowego
new
oraz pustych nawiasów nawiasów klamrowych lub nawiasów klamrowych
Inicjowanie wartości wykonuje następujące czynności:
w przypadku klas z co najmniej jednym publicznym konstruktorem jest wywoływany domyślny konstruktor
w przypadku klas niezwiązanych bez zadeklarowanych konstruktorów obiekt jest inicjowany zero, a konstruktor domyślny jest wywoływany
w przypadku tablic każdy element jest inicjowany przez wartość
we wszystkich innych przypadkach zmienna jest zainicjowana 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
}
Inicjowanie kopiowania
Inicjowanie kopiowania to inicjowanie jednego obiektu przy użyciu innego obiektu. Występuje w następujących przypadkach:
zmienna jest inicjowana przy użyciu znaku równości
argument jest przekazywany do funkcji
obiekt jest zwracany z funkcji
wyjątek jest zgłaszany lub przechwycony
element członkowski danych niestacjonalizowanych jest inicjowany przy użyciu znaku równości
składowe klasy, struktury i unii są inicjowane przez inicjowanie kopii podczas inicjowania agregacji. Zobacz Agregowanie inicjowania , aby zapoznać się z przykładami.
Poniższy kod przedstawia kilka przykładów inicjowania kopiowania:
#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();
}
}
Inicjowanie kopiowania nie może wywoływać jawnych konstruktorów.
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
W niektórych przypadkach, jeśli konstruktor kopiujący klasy jest usunięty lub niedostępny, inicjalizacja kopiująca powoduje błąd kompilatora.
Inicjowanie bezpośrednie
Inicjowanie bezpośrednie jest inicjowane przy użyciu nawiasów klamrowych (niepustych). W przeciwieństwie do inicjalizacji kopiującej, może wywołać jawne konstruktory. Występuje w następujących przypadkach:
zmienna jest inicjowana z niepustymi nawiasami klamrowymi lub nawiasami
zmienna jest inicjowana za pomocą słowa kluczowego
new
oraz nawiasów niepustych lub nawiasówzmienna jest inicjowana za pomocą polecenia
static_cast
w konstruktorze klasy bazowe i niestatyczne składowe są inicjowane przy użyciu listy inicjatorów
w kopii przechwyconej zmiennej wewnątrz wyrażenia lambda
Poniższy kod przedstawia kilka przykładów inicjowania bezpośredniego:
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();
}
Inicjowanie listy
Inicjowanie listy występuje, gdy zmienna jest inicjowana przy użyciu listy inicjatora nawiasów klamrowych. Listy inicjatorów nawiasów klamrowych mogą być używane w następujących przypadkach:
inicjowana jest zmienna
klasa jest inicjowana za pomocą słowa kluczowego
new
obiekt jest zwracany z funkcji
argument przekazany do funkcji
jeden z argumentów w inicjalizacji bezpośredniej
w inicjatorze niestacjonalnym składowym danych
na liście inicjatora konstruktora
Poniższy kod przedstawia kilka przykładów inicjowania 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' } };
}
Inicjowanie agregacji
Inicjalizacja agregacji jest formą inicjalizacji listy dla tablic lub typów klas (zwykle struktur lub unii), które:
brak prywatnych lub chronionych elementów członkowskich
brak konstruktorów dostarczonych przez użytkownika, z wyjątkiem jawnie domyślnych lub usuniętych konstruktorów
brak klas bazowych
brak wirtualnych funkcji składowych
Uwaga
W programie Visual Studio 2015 i starszych agregacja nie może mieć inicjatorów nawiasów klamrowych lub równych dla niestatycznych elementów członkowskich. To ograniczenie zostało usunięte w standardzie C++14 i zaimplementowane w programie Visual Studio 2017.Inicjatory agregacji składają się z listy inicjowania nawiasów klamrowych z znakiem równości lub bez znaku równości, jak w poniższym przykładzie:
#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;
}
Powinny zostać wyświetlone następujące dane wyjściowe:
agg1: c: 1
agg2: Z: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0
Ważne
Elementy członkowskie tablicy, które są zadeklarowane, ale nie są jawnie inicjowane podczas inicjowania agregacji, są inicjowane zero, jak pokazano myArr3
powyżej.
Inicjowanie związków i struktur
Jeśli unia nie ma konstruktora, możesz zainicjować ją za pomocą pojedynczej wartości (lub 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 inicjowanie związków i struktur w poniższym 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 zawierających 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, na przykład:
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'} };
}
Inicjowanie odwołania
Zmienne typu referencyjnego muszą być inicjowane z obiektem typu, który dziedziczy ten typ referencyjny lub obiektem typu, który może zostać przekonwertowany na typ, z którego dziedziczy typ referencyjny. Na przykład:
// 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
}
Jedynym sposobem, aby zainicjować odwołanie tymczasowego obiektu, jest zainicjowanie tymczasowego obiektu stałego. Po zainicjowaniu zmienna typu odwołania zawsze wskazuje ten sam obiekt; nie można go zmodyfikować w celu wskazania innego obiektu.
Mimo że składnia może być taka sama, zainicjowanie zmiennych typu odwołania i przypisanie do zmiennych typu odwołania są semantycznie różne. W poprzednim przykładzie, przydziały, które zmieniają iVar
i lVar
wyglądają podobnie do inicjalizacji, ale dają różne efekty. Inicjalizacja określa obiekt, na który wskazuje zmienna typu odwołania; przypisanie przypisuje do określonego obiektu poprzez odniesienie.
Ponieważ zarówno przekazywanie argumentu typu odwołania do funkcji i zwracanie wartości typu odwołania przez funkcję są inicjalizacjami, formalne argumenty do funkcji są inicjowane poprawnie podczas zwracania odwołania.
Zmienna typu odwołania może być deklarowana bez inicjatorów tylko w następujących przypadkach:
Deklaracje funkcji (prototypy). Na przykład:
int func( int& );
Deklaracje typu funkcji zwracanej. Na przykład:
int& func( int& );
Deklaracja składowej klasy typu odwołania. Na przykład:
class c {public: int& i;};
Deklaracja zmiennej jawnie określonej jako
extern
. Na przykład:extern int& iVal;
Podczas inicjowania zmiennej typu odwołania kompilator używa grafu decyzyjnego pokazanego na poniższej ilustracji, aby wybrać między utworzeniem odwołania do obiektu lub utworzeniem tymczasowego obiektu, do którego wskazuje odwołanie:
Wykres decyzyjny zaczyna się od: czy inicjator lvalue tego samego typu lub typu pochodzącego z typu odwołania? Jeśli tak, odwołanie odwołuje się do obiektu określonego w inicjatorze. Jeśli nie, następną decyzją jest to, czy zmienna typu odwołania jest zainicjowanym odwołaniem const T i czy inicjator może zostać niejawnie przekonwertowany na T? Jeśli tak, zostanie utworzona tymczasowa, a zmienna referencyjna stanie się nazwą tego tymczasowego. Jeśli nie, jest to błąd.
Wykres decyzyjny na potrzeby inicjowania typów referencyjnych
Odwołania do volatile
typów (zadeklarowanych jako volatile
typename&identifier) można zainicjować za pomocą volatile
obiektów tego samego typu lub obiektów, które nie zostały zadeklarowane jako volatile
. Nie można jednak zainicjować ich z obiektami const
tego typu. Podobnie odwołania do const
typów (zadeklarowanych jakoconst
typename&identifier) można zainicjować za pomocą const
obiektów tego samego typu (lub dowolnych elementów, które mają konwersję na ten typ lub z obiektami, które nie zostały zadeklarowane jako ).const
Nie można jednak zainicjować ich z obiektami volatile
tego typu.
Odwołania, które nie są kwalifikowane za pomocą słowa kluczowego const
lubvolatile
, mogą być inicjowane tylko z obiektami zadeklarowanymi jako ani const
ani .volatile
Inicjowanie zmiennych zewnętrznych
Deklaracje zmiennych automatycznych, statycznych i zewnętrznych mogą zawierać inicjatory. Jednak deklaracje zmiennych zewnętrznych mogą zawierać inicjatory tylko wtedy, gdy zmienne nie są deklarowane jako extern
.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla