Vor dem Standard C++11 musste man bei der Notwendigkeit, mehrere Konstruktoren mit unterschiedlichen Parametern zu schreiben, den Initialisierungscode in jedem von ihnen duplizieren, da es nicht möglich war, einen anderen Konstruktor innerhalb der Konstruktoren der Klasse aufzurufen.
Die Unmöglichkeit, bereits vorhandene Initialisierungslogik in anderen Konstruktoren zu verwenden, führte zur Code-Duplizierung und zu Fehlern bei Änderungen der Initialisierungslogik: Aktualisierte man einen Konstruktor, konnte es leicht passieren, dass man die anderen vergaß.
Mit der Einführung von C++11 entstand der Mechanismus der "delegierenden Konstruktoren", der es einem Konstruktor einer Klasse ermöglicht, einen anderen Konstruktor derselben Klasse über die Syntax der Initialisierungsliste aufzurufen.
Beispielcode:
class Widget { public: Widget() : Widget(0, "standard") {} Widget(int n) : Widget(n, "benutzer") {} Widget(int n, std::string name) : size(n), label(name) {} private: int size; std::string label; };
Jetzt ist die gesamte gemeinsame Logik in einem "Haupt"-Konstruktor konzentriert, alles andere delegiert den Aufruf.
Wichtige Merkmale:
Kann man den Aufruf eines anderen Konstruktors durch einen Funktionsaufruf innerhalb des Körpers des Konstruktors delegieren?
Nein. Der Aufruf eines Konstruktors von einem anderen Konstruktor ist nur über die Initialisierungsliste möglich und nicht innerhalb des Körpers des Konstruktors. Wenn man eine Funktion aus dem Körper des Konstruktors aufruft, sind die Mitglieder des Objekts bereits initialisiert.
Was passiert, wenn man einen "Ring" der Delegation zwischen Konstruktoren erstellt?
Zirkuläre Delegation (z.B. A delegiert an B und B an A) führt zu einem Kompilierungsfehler in C++: Es kann keine unendliche Rekursion zwischen den Konstruktoren geben.
Kann man den Aufruf eines Konstruktors der Basisklasse durch einen delegierenden Konstruktor der abgeleiteten Klasse delegieren?
Nein. Delegierende Konstruktoren funktionieren nur innerhalb einer Klasse. Für den Aufruf des Basiskonstruktors wird eine separate Syntax verwendet:
class Base { public: Base(int) {} }; class Derived : public Base { public: Derived() : Base(42) {} };
Drei Konstruktoren, die jedes Feld explizit initialisieren, die Logik ist nicht synchronisiert — die Änderung erfolgt nur in einem Konstruktor, die beiden anderen sind inkorrekt.
Vorteile:
Nachteile:
Ein Hauptkonstruktor, die anderen delegieren an ihn, die gesamte Initialisierungslogik ist zentralisiert.
Vorteile:
Nachteile: