Lista inicjatorów służy do inicjowania elementów danych klasy. Lista elementów do zainicjowania jest wskazywana przez konstruktora jako lista oddzielona przecinkami, po której następuje dwukropek. Poniżej znajduje się przykład użycia listy inicjatorów do inicjalizacji x i y klasy Point.
Przykład
C++
#include> using> namespace> std;> class> Point {> private>:> >int> x;> >int> y;> public>:> >Point(>int> i = 0,>int> j = 0): x(i), y(j) {}> >/* The above use of Initializer list is optional as the> >constructor can also be written as:> >Point(int i = 0, int j = 0) {> >x = i;> >y = j;> >}> >*/> >int> getX()>const> {>return> x; }> >int> getY()>const> {>return> y; }> };> int> main()> {> >Point t1(10, 15);> >cout <<>'x = '> << t1.getX() <<>', '>;> >cout <<>'y = '> << t1.getY();> >return> 0;> }> |
>
>Wyjście
x = 10, y = 15>
Powyższy kod jest tylko przykładem składni listy inicjatorów. W powyższym kodzie x i y można również łatwo parafować wewnątrz konstruktora. Są jednak sytuacje, w których inicjalizacja elementów danych wewnątrz konstruktora nie działa i należy użyć listy inicjatorów. Oto takie przypadki:
1. Do inicjalizacji niestatycznych stałych elementów danych
const elementy danych muszą zostać zainicjowane przy użyciu listy inicjatorów. W poniższym przykładzie t jest stałym elementem danych klasy Test i jest inicjowany przy użyciu listy inicjatorów. Powodem inicjowania stałego elementu danych na liście inicjatorów jest to, że dla stałego elementu danych nie jest przydzielana oddzielnie pamięć, jest ona złożona w tablicy symboli, przez co musimy ją zainicjować na liście inicjatorów.
Ponadto jest to konstruktor sparametryzowany i nie musimy wywoływać operatora przypisania, co oznacza, że unikamy jednej dodatkowej operacji.
Przykład
C++
// C++ progmram to demonstrate the use of> // initializer list to initialize the const> // data member> #include> using> namespace> std;> class> Test {> >const> int> t;> public>:> >//Initializer list must be used> >Test(>int> t):t(t) {}> >int> getT() {>return> t; }> };> int> main() {> >Test t1(10);> >cout< return 0; }> |
>
>Wyjście
10>
2. Do inicjalizacji elementów referencyjnych
Elementy referencyjne muszą zostać zainicjowane przy użyciu listy inicjatorów. W poniższym przykładzie t jest członkiem referencyjnym klasy Test i jest inicjowany przy użyciu listy inicjatorów.
Przykład
C++
Python konwertuje bajty na ciąg znaków
// Initialization of reference data members> #include> using> namespace> std;> class> Test {> >int> &t;> public>:> >Test(>int> &t):t(t) {}>//Initializer list must be used> >int> getT() {>return> t; }> };> int> main() {> >int> x = 20;> >Test t1(x);> >cout< x = 30; cout< return 0; }> |
>
>Wyjście
20 30>
3. Do inicjalizacji obiektów członkowskich, które nie mają konstruktora domyślnego
W poniższym przykładzie obiekt a klasy A jest elementem danych klasy B, a obiekt A nie ma domyślnego konstruktora. Do inicjalizacji pliku należy użyć listy inicjatorów.
Przykład
C++
// C++ progmam to initialize a member object without default> // constructor> #include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int>);> };> A::A(>int> arg)> {> >i = arg;> >cout <<>'A's Constructor called: Value of i: '> << i> ><< endl;> }> // Class B contains object of A> class> B {> >A a;> public>:> >B(>int>);> };> B::B(>int> x) : a(x)> {>// Initializer list must be used> >cout <<>'B's Constructor called'>;> }> int> main()> {> >B obj(10);> >return> 0;> }> |
>
>Wyjście
A's Constructor called: Value of i: 10 B's Constructor called>
Jeśli klasa A miała zarówno konstruktory domyślne, jak i sparametryzowane, wówczas lista inicjatorów nie jest koniecznością, jeśli chcemy zainicjować konstruktor domyślny, ale konieczne jest zainicjowanie konstruktora sparametryzowanego.
4. Do inicjalizacji składowych klasy bazowej
Podobnie jak w punkcie 3, sparametryzowany konstruktor klasy bazowej można wywołać jedynie przy użyciu listy inicjatorów.
Przykład
C++
#include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int> );> };> A::A(>int> arg) {> >i = arg;> >cout <<>'A's Constructor called: Value of i: '> << i << endl;> }> // Class B is derived from A> class> B: A {> public>:> >B(>int> );> };> B::B(>int> x):A(x) {>//Initializer list must be used> >cout <<>'B's Constructor called'>;> }> int> main() {> >B obj(10);> >return> 0;> }> |
>
>Wyjście
A's Constructor called: Value of i: 10 B's Constructor called>
5. Gdy nazwa parametru konstruktora jest taka sama jak element danych
Jeśli nazwa parametru konstruktora jest taka sama jak nazwa elementu danych, wówczas element danych musi zostać zainicjalizowany za pomocą ten wskaźnik lub Lista inicjatorów. W poniższym przykładzie zarówno nazwa elementu, jak i nazwa parametru A() to i.
Przykład
C++
#include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int>);> >int> getI()>const> {>return> i; }> };> A::A(>int> i) : i(i)> {> }>// Either Initializer list or this pointer must be used> /* The above constructor can also be written as> A::A(int i) {> >this->ja = ja;> }> */> int> main()> {> >A a(10);> >cout << a.getI();> >return> 0;> }> |
>
>Wyjście
10>
6. Ze względów wydajnościowych
Lepiej jest zainicjować wszystkie zmienne klasy na liście inicjatorów, zamiast przypisywać wartości wewnątrz treści. Rozważ następujący przykład:
Przykład
C++
// Without Initializer List> class> MyClass {> >Type variable;> public>:> >MyClass(Type a) {>// Assume that Type is an already> >// declared class and it has appropriate> >// constructors and operators> >variable = a;> >}> };> |
>
>
Tutaj kompilator wykonuje następujące kroki, aby utworzyć obiekt typu MyClass
1. Konstruktor typu jest wywoływany jako pierwszy dla a.
2. Domyślna zmienna konstrukcyjna
3. Operator przypisania Type jest wywoływany wewnątrz konstruktora MyClass() w celu przypisania
variable = a;>
4. I wreszcie wywoływany jest destruktor typu a, ponieważ wykracza on poza zakres.
Teraz rozważ ten sam kod z konstruktorem MyClass() z listą inicjatorów
C++
czy android może grać w gamepigeon
// With Initializer List> class> MyClass {> >Type variable;> public>:> >MyClass(Type a):variable(a) {>// Assume that Type is an already> >// declared class and it has appropriate> >// constructors and operators> >}> };> |
>
>
W przypadku listy inicjatorów kompilator wykonuje następujące kroki:
1. Konstruktor typu jest wywoływany jako pierwszy dla a.
2. Wywoływany jest sparametryzowany konstruktor klasy Type w celu zainicjowania: zmiennej(a). Argumenty na liście inicjatorów służą do bezpośredniego kopiowania zmiennej konstrukcyjnej.
3. Destruktor typu jest wywoływany, ponieważ wykracza poza zakres.
Jak widać z tego przykładu, jeśli używamy przypisania w treści konstruktora, istnieją trzy wywołania funkcji: konstruktor + destruktor + jedno wywołanie operatora przypisania dodawania. A jeśli użyjemy listy inicjatorów, istnieją tylko dwa wywołania funkcji: konstruktor kopiujący + wywołanie destruktora. Zobacz ten post, aby zapoznać się z bieżącym przykładem w tej kwestii.
Ta kara za przypisanie będzie znacznie większa w rzeczywistych zastosowaniach, w których będzie wiele takich zmiennych. Dzięki pt za dodanie tego punktu.
Parametr a jednolita inicjalizacja w C++
Lepiej jest używać listy inicjalizacji z jednolitą inicjalizacją {} niż inicjalizacji parametrów (), aby uniknąć problemu zawężania konwersji i nieoczekiwanego zachowania. Zapewnia bardziej rygorystyczne sprawdzanie typu podczas inicjalizacji i zapobiega potencjalnym zawężającym konwersjom
Kod wykorzystujący inicjalizację parametrów ()
C++
#include> class> Base {> >char> x;> public>:> >Base(>char> a)> >: x{ a }> >{> >}> >void> print() { std::cout <<>static_cast><>int>>(X); }> };> int> main()> {> >Base b{ 300 };>// Using uniform initialization with {}> >b.print();> >return> 0;> }> |
>
>Wyjście
44>
W powyższym kodzie wartość 300 jest poza prawidłowym zakresem dla znaku, co może prowadzić do niezdefiniowanego zachowania i potencjalnie nieprawidłowych wyników. Kompilator może wygenerować ostrzeżenie lub błąd w tej sytuacji, w zależności od ustawień kompilacji.
Kod wykorzystujący jednolitą inicjalizację {}
Używając jednolitej inicjalizacji za pomocą {} i inicjując x podaną wartością a, kompilator przeprowadzi bardziej rygorystyczną kontrolę typu i wygeneruje ostrzeżenie lub błąd podczas kompilacji, wskazując zawężającą konwersję z int na char.
Oto kod z jednolitą inicjalizacją {} , co skutkuje ostrzeżeniem i dlatego jest lepsze w użyciu
C++
#include> class> Base {> >char> x;> public>:> >Base(>char> a)> >: x{ a }> >{> >}> >void> print() { std::cout <<>static_cast><>int>>(X); }> };> int> main()> {> >Base b{ 300 };>// Using uniform initialization with {}> >b.print();> >return> 0;> }> |
>
>
main.cpp: In function ‘int main()’: main.cpp:17:17: error: narrowing conversion of ‘300’ from ‘int’ to ‘char’ [-Wnarrowing] 17 | Base b{ 300 }; // Using uniform initialization with {} | ^>