Menu
Coddy logo textTech

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ń.

challenge icon

Wyzwanie

Łatwy

Zbudujmy 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 utworzeniu
    • const int maxPlayers: maksymalna liczba graczy, ustalana podczas tworzenia obiektu
    • int& difficultyRef: referencję do zewnętrznego ustawienia poziomu trudności
    • int screenWidth i int 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ą int przechowującą poziom trudności, do której będzie się odwoływać obiekt config
    • Utworzyć obiekt GameConfig za 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;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online