Konstruktor-Initialisierungslisten
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 22 von 104.
Sie haben die Doppelpunkt-Syntax in vorherigen Lektionen gesehen, wie zum Beispiel Player(std::string n, int h) : name(n), health(h) {}. Dies wird als Elementinitialisierungsliste bezeichnet und ist die bevorzugte Methode, um Member in C++ zu initialisieren.
Die Initialisierungsliste erscheint nach der Parameterliste des Konstruktors, beginnend mit einem Doppelpunkt. Jedes Mitglied wird direkt mit seinem Wert initialisiert, getrennt durch Kommas:
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) {
// Konstruktor-Rumpf (kann leer sein)
}
};Warum sollte man Initialisierungslisten anstelle von Zuweisungen im Konstruktorrumpf verwenden? Bei der Zuweisung werden Member zuerst standardmäßig konstruiert und anschließend werden ihnen neue Werte zugewiesen. Mit Initialisierungslisten werden Member direkt mit den korrekten Werten konstruiert – das ist effizienter und in manchen Fällen zwingend erforderlich.
Bestimmte Member müssen über die Liste initialisiert werden:
const-Member – können nach der Konstruktion nicht mehr zugewiesen werden
- Referenz-Member – müssen bei der Initialisierung gebunden werden
- Member ohne Standardkonstruktoren
class Player {
const int maxHealth; // Muss Initialisierungsliste verwenden
std::string name;
public:
// Das funktioniert
Player(int max, std::string n) : maxHealth(max), name(n) {}
// Dies würde NICHT kompilieren:
// Player(int max) { maxHealth = max; } // Fehler!
};Elemente werden in der Reihenfolge initialisiert, in der sie in der Klasse deklariert sind, nicht in der Reihenfolge in der Initialisierungsliste. Schreiben Sie Ihre Initialisierungsliste immer in der Deklarationsreihenfolge, um Verwirrung zu vermeiden.
Aufgabe
EinfachLassen Sie uns ein Konfigurationssystem für eine Game-Engine erstellen, das demonstriert, wann und warum Elementinitialisierungslisten (Member Initializer Lists) unerlässlich sind. Sie werden eine GameConfig-Klasse mit Elementen erstellen, die mithilfe der Initialisierungslisten-Syntax initialisiert werden müssen.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
GameConfig.h: Definieren Sie eineGameConfig-Klasse, die Spieleinstellungen speichert. Ihre Klasse sollte Folgendes enthalten:- Ein
const std::string gameName— der Titel des Spiels, der sich nach der Erstellung nicht mehr ändern kann - Ein
const int maxPlayers— die maximale Spieleranzahl, die bei der Konstruktion festgelegt wird - Ein
int& difficultyRef— eine Referenz auf eine externe Schwierigkeitseinstellung - Ein
int screenWidthundint screenHeight— reguläre Elemente für Anzeigeeinstellungen - Einen Konstruktor, der alle notwendigen Parameter entgegennimmt und jedes Element mithilfe der Initialisierungsliste initialisiert. Die Initialisierungsliste sollte der Reihenfolge folgen, in der die Elemente in der Klasse deklariert sind
- Eine
display()-Methode, welche die Konfiguration in diesem Format ausgibt:Game: <gameName> Max Players: <maxPlayers> Difficulty: <difficultyRef value> Resolution: <screenWidth>x<screenHeight>
- Ein
main.cpp: Lesen Sie Konfigurationswerte aus der Eingabe ein und erstellen Sie ein GameConfig-Objekt. Sie müssen:- Den Spielnamen (string), die maximale Spieleranzahl (int), die Schwierigkeit (int), die Breite (int) und die Höhe (int) aus der Eingabe lesen — jeweils in einer separaten Zeile
- Eine lokale
int-Variable für die Schwierigkeit erstellen, auf die die Konfiguration verweisen wird - Ein
GameConfig-Objekt unter Verwendung des Initialisierungslisten-Konstruktors erstellen display()aufrufen, um die initiale Konfiguration anzuzeigen- Die Schwierigkeitsvariable direkt ändern (mit 2 multiplizieren)
"After difficulty change:"ausgebendisplay()erneut aufrufen, um zu zeigen, dass die Referenz den aktualisierten Wert widerspiegelt
Diese Herausforderung verdeutlicht, warum Initialisierungslisten wichtig sind: Ihre const-Elemente und das Referenz-Element können nicht im Konstruktorrumpf zugewiesen werden — sie müssen in der Liste initialisiert werden. Das Referenz-Element zeigt zudem, wie Änderungen an der ursprünglichen Variable durch das Konfigurationsobjekt reflektiert werden.
Binden Sie Ihre Header-Datei in main.cpp mit #include "GameConfig.h" ein.
Spickzettel
Eine Elementinitialisierungsliste ist die bevorzugte Art, Klassen-Member in C++ zu initialisieren. Sie erscheint nach der Parameterliste des Konstruktors, beginnt mit einem Doppelpunkt, wobei jedes Member direkt initialisiert wird:
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {
// Konstruktor-Rumpf
}
};Warum Initialisierungslisten verwenden? Member werden direkt mit den korrekten Werten konstruiert, anstatt standardmäßig konstruiert und anschließend zugewiesen zu werden, was effizienter ist.
Erforderlich für bestimmte Member:
const-Member - können nach der Konstruktion nicht mehr zugewiesen werden- Referenz-Member - müssen bei der Initialisierung gebunden werden
- Member ohne Standardkonstruktoren
class Player {
const int maxHealth;
int& reference;
public:
// Korrekt - verwendet Initialisierungsliste
Player(int max, int& r) : maxHealth(max), reference(r) {}
// Fehler - const oder Referenz kann nicht im Rumpf zugewiesen werden
// Player(int max) { maxHealth = max; }
};Wichtig: Member werden in der Reihenfolge initialisiert, in der sie in der Klasse deklariert sind, nicht in der Reihenfolge in der Initialisierungsliste. Schreiben Sie Ihre Initialisierungsliste immer in der Deklarationsreihenfolge.
Probier es selbst
#include <iostream>
#include <string>
#include "GameConfig.h"
using namespace std;
int main() {
// Eingabewerte lesen
string gameName;
getline(cin, gameName);
int maxPlayers;
cin >> maxPlayers;
int difficulty;
cin >> difficulty;
int width;
cin >> width;
int height;
cin >> height;
// TODO: Erstelle ein GameConfig-Objekt unter Verwendung des Initialisierungslisten-Konstruktors
// Der Konstruktor sollte empfangen: gameName, maxPlayers, difficulty (per Referenz), width, height
// TODO: Rufe display() auf, um die ursprüngliche Konfiguration anzuzeigen
// TODO: Ändere die Variable difficulty direkt (multipliziere sie mit 2)
// TODO: Gib "After difficulty change:" aus
// TODO: Rufe display() erneut auf, um die aktualisierte Konfiguration anzuzeigen
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container