Listy inicjalizacyjne konstruktora
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 22 z 104.
W poprzednich lekcjach widziałeś składnię z dwukropkiem, na przykład Player(std::string n, int h) : name(n), health(h) {}. Nazywa się ją listą inicjalizacyjną składowych i jest preferowanym sposobem inicjalizowania składowych w C++.
Lista inicjalizacyjna występuje po liście parametrów konstruktora i zaczyna się dwukropkiem. Każdy składowy jest inicjalizowany bezpośrednio swoją wartością, a poszczególne elementy są oddzielone przecinkami:
class Rectangle {
int width;
int height;
const int id;
int& reference;
public:
Rectangle(int w, int h, int i, int& r)
: width(w), height(h), id(i), reference(r) {
// Ciało konstruktora (może być puste)
}
};Dlaczego w treści konstruktora używać list inicjalizacyjnych zamiast przypisania? Przy przypisaniu składowe są najpierw konstruowane domyślnie, a następnie otrzymują nowe wartości. Dzięki listom inicjalizacyjnym składowe są konstruowane bezpośrednio z prawidłowymi wartościami — jest to wydajniejsze, a czasem wręcz wymagane.
Pewne składowe muszą być inicjalizowane za pomocą listy:
- elementy
const— nie można im przypisywać wartości po utworzeniu
- Referencje — muszą być powiązane podczas inicjalizacji
- Elementy bez konstruktorów domyślnych
class Player {
const int maxHealth; // Trzeba użyć listy inicjalizacyjnej
std::string name;
public:
// To działa
Player(int max, std::string n) : maxHealth(max), name(n) {}
// To by się NIE skompilowało:
// Player(int max) { maxHealth = max; } // Błąd!
};Składowe są inicjalizowane w kolejności, w jakiej zostały zadeklarowane w klasie, a nie w kolejności na liście inicjalizacyjnej. Zawsze zapisuj listę inicjalizacyjną w kolejności deklaracji, aby uniknąć nieporozumień.
Wyzwanie
ŁatwyZbudujmy system konfiguracji dla silnika gry, który pokazuje, kiedy i dlaczego listy inicjalizacyjne składowych są niezbędne. Utworzysz klasę GameConfig ze składowymi, które muszą być inicjalizowane za pomocą składni listy inicjalizacyjnej.
Utworzysz dwa pliki, aby uporządkować kod:
GameConfig.h: Zdefiniuj klasęGameConfig, która przechowuje ustawienia gry. Twoja klasa powinna zawierać:const std::string gameName: tytuł gry, którego nie można zmienić po utworzeniuconst int maxPlayers: maksymalna liczba graczy, ustalana podczas tworzenia obiektuint& difficultyRef: referencję do zewnętrznego ustawienia poziomu trudnościint screenWidthiint screenHeight: zwykłe składowe przechowujące ustawienia wyświetlania- Konstruktor, który przyjmuje wszystkie niezbędne parametry i inicjalizuje każdą składową za pomocą listy inicjalizacyjnej. Lista inicjalizacyjna powinna zachowywać kolejność deklaracji składowych w klasie
- Metodę
display(), która wyświetla konfigurację w tym formacie:Game: <gameName> Max Players: <maxPlayers> Difficulty: <difficultyRef value> Resolution: <screenWidth>x<screenHeight>
main.cpp: Odczytaj wartości konfiguracji z wejścia i utwórz obiekt GameConfig. Potrzebujesz:- Odczytać z wejścia nazwę gry (string), maksymalną liczbę graczy (int), poziom trudności (int), szerokość (int) i wysokość (int): każdą wartość w osobnym wierszu
- Utworzyć lokalną zmienną
intprzechowującą poziom trudności, do której będzie się odwoływać obiekt config - Utworzyć obiekt
GameConfigza pomocą konstruktora z listą inicjalizacyjną - Wywołać
display(), aby wyświetlić początkową konfigurację - Zmodyfikować bezpośrednio zmienną poziomu trudności (pomnożyć ją przez 2)
- Wyświetlić
"After difficulty change:" - Ponownie wywołać
display(), aby pokazać, że referencja odzwierciedla zaktualizowaną wartość
To wyzwanie pokazuje, dlaczego listy inicjalizacyjne są ważne: składowych const i składowej będącej referencją nie można przypisywać w treści konstruktora. Muszą być inicjalizowane na liście. Składowa będąca referencją pokazuje również, jak zmiany w oryginalnej zmiennej są odzwierciedlane w obiekcie config.
Dołącz plik nagłówkowy w main.cpp za pomocą #include "GameConfig.h".
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "GameConfig.h"
using namespace std;
int main() {
// Wczytaj wartości wejściowe
string gameName;
getline(cin, gameName);
int maxPlayers;
cin >> maxPlayers;
int difficulty;
cin >> difficulty;
int width;
cin >> width;
int height;
cin >> height;
// TODO: Utwórz obiekt GameConfig za pomocą konstruktora z listą inicjalizacyjną
// Konstruktor powinien otrzymać: gameName, maxPlayers, difficulty (przez referencję), width, height
// TODO: Wywołaj display(), aby wyświetlić początkową konfigurację
// TODO: Zmień bezpośrednio zmienną difficulty (pomnóż ją przez 2)
// TODO: Wypisz "After difficulty change:"
// TODO: Wywołaj ponownie display(), aby wyświetlić zaktualizowaną konfigurację
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online