Historia pytania:
Lista inicjalizacji pojawiła się w C++ w celu optymalizacji i poprawnej inicjalizacji członków klasy przed wykonaniem głównej treści konstruktora. Wynika to z doświadczeń C i C++: dla stałych członów i referencji w ciele konstruktora nie można ich zainicjalizować, tylko w liście.
Problem:
Przy próbie inicjalizacji takich członów w ciele konstruktora zamiast w liście inicjalizacji wystąpi błąd kompilacji. Ignorowanie listy prowadzi również do podwójnej inicjalizacji dla obiektów-członków, co wpływa na wydajność, zwłaszcza przy skomplikowanych konstruktorach-inicjalizatorach.
Rozwiązanie:
Optymalnie jest zadeklarować i zainicjalizować członków klasy przez listę inicjalizacji — szczególnie jeśli są to stałe (const) lub referencje (&). Lista inicjalizacji jest aktywowana PRZED wykonaniem ciała konstruktora, kiedy członkowie klasy są jeszcze konstruowani.
Przykład kodu:
class Example { const int value; int& ref; public: Example(int v, int& r) : value(v), ref(r) { /* ciało konstruktora */ } };
Kluczowe cechy:
Co się stanie, jeśli zmienimy kolejność członków klasy i ich inicjalizacji w liście?
Inicjalizacja zawsze następuje w kolejności deklaracji członków w klasie, a nie w kolejności, w jakiej jest napisana w liście inicjalizacji. Jeśli zależne człony są inicjalizowane "w niewłaściwej kolejności", możliwe jest odwołanie do jeszcze nieinicjalizowanej pamięci.
Przykład kodu:
class Foo { int x; int y; Foo() : y(2), x(y) {} // x będzie zainicjalizowane PIERWSZE, wartością nieinicjalizowanego y }
Czy można zainicjalizować const-członek klasy w ciele konstruktora?
Nie. Spowoduje to błąd kompilacji. Tylko przez listę inicjalizacji.
Co się stanie, jeśli nie zainicjalizujemy członka-referencji?
Jeśli nie zainicjalizujemy członka-referencji, wystąpi błąd kompilacji, ponieważ referencja musi być "przypięta" do obiektu w momencie tworzenia i nie może być później zmieniana.
W klasie do przechowywania ustawień używana jest const std::string i referencja, próba inicjalizacji tych członów odbywa się w ciele konstruktora, kompilator zgłasza błąd lub dane nie są inicjalizowane.
Zalety: Nowicjusz nauczył się, jak odróżniać konstruowanie od przypisania.
Wady: Błąd kompilacji, niemożność używania klasy, możliwe nieprzewidziane błędy na etapie inicjalizacji obiektów.
Stała i referencja są poprawnie inicjalizowane przez listę inicjalizacji. Cały skomplikowany kod inicjalizacyjny jest skoncentrowany w liście, co zwiększa czytelność i zapobiega błędom.
Zalety: Bezpieczna i poprawna inicjalizacja członków klasy, wysoka czytelność, brak wycieków lub UB.
Wady: Może wystąpić skomplikowana logika inicjalizacji, część której nie jest tak łatwa do przetestowania bez dodatkowego kodu.