Constructors and member initializer lists
Konstruktoren sind nicht-statische Memberfunktionen , die mit einer speziellen Deklaratorsyntax deklariert werden. Sie werden verwendet, um Objekte ihrer Klassentypen zu initialisieren.
|
Ein Konstruktor kann keine Coroutine sein. |
(since C++20) |
|
Ein Konstruktor kann keinen explicit object parameter haben. |
(since C++23) |
Inhaltsverzeichnis |
Syntax
Konstruktoren werden mit Funktionsdeklaratoren als Mitgliedsfunktionen in der folgenden Form deklariert:
class-name
(
parameter-list
(optional)
)
except
(optional)
attr
(optional)
|
|||||||||
| class-name | - | ein Identifikatorausdruck , möglicherweise gefolgt von einer Liste von Attributen , und (seit C++11) möglicherweise eingeschlossen in ein Klammerpaar | ||||||
| parameter-list | - | Parameterliste | ||||||
| except | - |
|
||||||
| attr | - | (seit C++11) eine Liste von Attributen |
Die einzigen Spezifizierer, die in den
Deklarationsspezifizierern
einer Konstruktordeklaration erlaubt sind, sind
friend
,
inline
,
constexpr
(seit C++11)
,
consteval
(seit C++20)
und
explicit
(insbesondere ist kein Rückgabetyp erlaubt). Beachten Sie, dass
CV- und Referenzqualifizierer
ebenfalls nicht erlaubt sind: Die const- und volatile-Semantik eines im Bau befindlichen Objekts greift erst nach Abschluss des am meisten abgeleiteten Konstruktors.
Der Bezeichnerausdruck von class-name muss eine der folgenden Formen haben:
- In einer friend-Deklaration ist der Identifikatorausdruck ein qualifizierter Identifikator , der einen Konstruktor benennt .
- Andernfalls, in einer Member-Deklaration, die zur Member-Spezifikation einer Klasse oder Klassenvorlage gehört:
-
- Für Klassen ist der Identifikatorausdruck der injected-class-name der unmittelbar umschließenden Klasse.
- Für Klassentemplates ist der Identifikatorausdruck ein Klassenname, der die current instantiation (bis C++20) der injected-class-name (seit C++20) des unmittelbar umschließenden Klassentemplates.
- Andernfalls ist der Bezeichnerausdruck ein qualifizierter Bezeichner, dessen terminaler unqualifizierter Bezeichner der injizierte Klassenname seines Lookup -Kontexts ist.
Member-Initialisierungsliste
Der Rumpf einer
Funktionsdefinition
jedes Konstruktors der Klasse
T
, vor der öffnenden Klammer der Verbundanweisung, kann die
Member-Initialisierer-Liste
enthalten, deren Syntax das Doppelpunkt-Zeichen
:
ist, gefolgt von der kommagetrennten Liste von einem oder mehreren
Member-Initialisierer
n, von denen jeder die folgende Syntax hat:
| Member-Initialisierer | (1) | ||||||||
| Klassen-Initialisierer | (2) | ||||||||
Klassen-Pack
Initialisierer
...
|
(3) | (seit C++11) | |||||||
|
(since C++11) |
-
Eine direkte Basisklasse oder eine
virtual base class
von
T. In diesem Fall wird das entsprechende Basisklassen-Subobjekt direkt mit initializer initialisiert.
| member | - | ein Bezeichner, der ein Datenelement benennt |
| class | - | ein Klassenname |
| class-pack | - | ein Paket, das zu null oder mehr Klassen expandiert |
| initializer | - |
ein
Initialisierer
, der nicht mit
=
beginnt
|
struct S { int n; S(int); // Konstruktordeklaration S() : n(7) {} // Konstruktordefinition: // ": n(7)" ist die Initialisierungsliste // ": n(7) {}" ist der Funktionsrumpf }; S::S(int x) : n{x} {} // Konstruktordefinition: ": n{x}" ist die Initialisierungsliste int main() { S s; // ruft S::S() auf S s2(10); // ruft S::S(int) auf }
Erklärung
Konstruktoren haben keine Namen und können nicht direkt aufgerufen werden. Sie werden aufgerufen, wenn Initialisierung stattfindet, und sie werden gemäß den Regeln der Initialisierung ausgewählt. Die Konstruktoren ohne explicit -Spezifizierer sind converting constructors . Die Konstruktoren mit einem constexpr -Spezifizierer machen ihren Typ zu einem literal type . Konstruktoren, die ohne Argument aufgerufen werden können, sind default constructors . Konstruktoren, die ein anderes Objekt desselben Typs als Argument nehmen, sind copy constructors und move constructors .
Bevor der zusammengesetzte Anweisungsblock, der den Funktionsrumpf des Konstruktors bildet, mit der Ausführung beginnt, ist die Initialisierung aller direkten Basisklassen, virtuellen Basisklassen und nicht-statischen Datenelemente abgeschlossen. Die Member-Initialisierungsliste ist der Ort, an dem die nicht-standardmäßige Initialisierung dieser Teilobjekte angegeben werden kann. Für Basisklassen, die nicht standardmäßig initialisiert werden können, und für nicht-statische Datenelemente, die nicht durch Standardinitialisierung oder durch ihren default member initializer , falls vorhanden (since C++11) initialisiert werden können, wie z.B. Elemente von Referenz- und const-qualifizierten Typen, müssen Member-Initialisierer angegeben werden. (Beachten Sie, dass Standard-Member-Initialisierer für nicht-statische Datenelemente von Klassentemplate-Instanziierungen ungültig sein können, wenn der Elementtyp oder Initialisierer abhängig ist.) (since C++11) Keine Initialisierung wird durchgeführt für anonymous unions oder variant members , die keinen Member-Initialisierer haben oder default member initializer (since C++11) .
Die Initialisierer, bei denen class eine virtuelle Basisklasse benennt, werden während der Konstruktion jeder Klasse ignoriert, die nicht die am stärksten abgeleitete Klasse des zu konstruierenden Objekts ist.
Namen, die in einem initializer erscheinen, werden im Gültigkeitsbereich des Konstruktors ausgewertet:
class X { int a, b, i, j; public: const int& r; X(int i) : r(a) // initialisiert X::r als Referenz auf X::a , b{i} // initialisiert X::b mit dem Wert des Parameters i , i(i) // initialisiert X::i mit dem Wert des Parameters i , j(this->i) // initialisiert X::j mit dem Wert von X::i {} };
Ausnahmen, die von Member-Initialisierern geworfen werden, können durch einen Funktions- try Block behandelt werden.
|
Wenn ein nicht-statisches Datenelement sowohl einen Standard-Member-Initialisierer besitzt als auch in einer Member-Initialisiererliste erscheint, wird der Member-Initialisierer verwendet und der Standard-Member-Initialisierer ignoriert: struct S { int n = 42; // default member initializer S() : n(7) {} // will set n to 7, not 42 }; |
(since C++11) |
Referenzmitglieder können nicht an Temporärobjekte in einer Member-Initialisierungsliste gebunden werden:
struct A { A() : v(42) {} // Fehler const int& v; };
Hinweis: Gleiches gilt für default member initializer .
Operationen während Konstruktion und Destruktion
Memberfunktionen (einschließlich
virtueller Memberfunktionen
) können für ein Objekt während seiner Konstruktion oder Destruktion aufgerufen werden. Ebenso kann ein Objekt während seiner Konstruktion oder Destruktion der Operand von
typeid
oder
dynamic_cast
sein.
Wenn diese Operationen jedoch während einer der folgenden Auswertungen durchgeführt werden, ist das Verhalten undefiniert:
|
(seit C++26) |
- eine Auswertung eines Member-Initialisiererlistens bevor alle member-initializer s für Basisklassen abgeschlossen sind
Delegierender KonstruktorWenn der Klassenname selbst als class-or-identifier in der Member-Initialisierungsliste erscheint, dann darf die Liste nur aus diesem einen Member-Initialisierer bestehen; ein solcher Konstruktor wird als delegierender Konstruktor bezeichnet, und der durch das einzige Element der Initialisierungsliste ausgewählte Konstruktor ist der Zielkonstruktor . In diesem Fall wird der Zielkonstruktor durch Überladungsauflösung ausgewählt und zuerst ausgeführt, dann kehrt die Steuerung zum delegierenden Konstruktor zurück und dessen Rumpf wird ausgeführt. Delegierende Konstruktoren können nicht rekursiv sein. class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int) }; Vererbende KonstruktorenSiehe using Deklaration . |
(seit C++11) |
Initialisierungsreihenfolge
Die Reihenfolge der Member-Initialisierer in der Liste ist irrelevant, die tatsächliche Reihenfolge der Initialisierung ist wie folgt:
(Hinweis: Wenn die Initialisierungsreihenfolge durch das Erscheinen in den Member-Initialisierungslisten verschiedener Konstruktoren gesteuert würde, dann könnte der Destruktor nicht sicherstellen, dass die Reihenfolge der Zerstörung die Umkehrung der Konstruktionsreihenfolge ist.)
Hinweise
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_delegating_constructors
|
200604L
|
(C++11) | Delegierende Konstruktoren |
Beispiel
#include <fstream> #include <string> #include <mutex> struct Base { int n; }; struct Class : public Base { unsigned char x; unsigned char y; std::mutex m; std::lock_guard<std::mutex> lg; std::fstream f; std::string s; Class(int x) : Base{123}, // Basisklasse initialisieren x(x), // x (Member) wird mit x (Parameter) initialisiert y{0}, // y wird auf 0 initialisiert f{"test.cc", std::ios::app}, // dies erfolgt nach der Initialisierung von m und lg s(__func__), // __func__ ist verfügbar, da die Init-Liste Teil des Konstruktors ist lg(m), // lg verwendet m, das bereits initialisiert wurde m{} // m wird vor lg initialisiert, obwohl es hier zuletzt erscheint {} // leerer zusammengesetzter Ausdruck Class(double a) : y(a + 1), x(y), // x wird vor y initialisiert, sein Wert hier ist unbestimmt lg(m) {} // Basisklassen-Initialisierer erscheint nicht in der Liste, sie wird // standardinitialisiert (nicht dasselbe wie bei Base(), was Wertinitialisierung wäre) Class() try // Funktions-Try-Block beginnt vor dem Funktionsrumpf, der die Init-Liste enthält : Class(0.0) // delegierender Konstruktor { // ... } catch (...) { // Ausnahme während der Initialisierung aufgetreten } }; int main() { Class c; Class c1(1); Class c2(0.1); }
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 194 | C++98 |
die Deklaratorsyntax von Konstruktoren erlaubte
höchstens einen Funktionsspezifizierer (z.B. konnte ein Konstruktor nicht als inline explicit deklariert werden) |
mehrere Funktionsspezifizierer
erlaubt |
| CWG 257 | C++98 |
es war nicht spezifiziert, ob eine abstrakte Klasse
Member-Initialisierer für ihre virtuellen Basisklassen bereitstellen muss |
als nicht erforderlich spezifiziert
und solche Member-Initialisierer werden während der Ausführung ignoriert |
| CWG 263 | C++98 |
die Deklaratorsyntax von Konstruktoren
verbot, dass Konstruktoren als Friends deklariert werden |
erlaubt, dass Konstruktoren
als Friends deklariert werden |
| CWG 1345 | C++98 |
anonyme Union-Member ohne Standard-
Member-Initialisierer wurden standardinitialisiert |
sie werden nicht initialisiert |
| CWG 1435 | C++98 |
die Bedeutung von "Klassenname" in der
Deklaratorsyntax von Konstruktoren war unklar |
änderte die Syntax zu einer spezialisierten
Funktionsdeklarator-Syntax |
| CWG 1696 | C++98 |
Referenz-Member konnten auf Temporärwerte initialisiert werden
(deren Lebensdauer am Ende des Konstruktors enden würde) |
solche Initialisierung
ist fehlerhaft |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 11.4.5 Konstruktoren [class.ctor]
-
- 11.9.3 Initialisierung von Basisklassen und Mitgliedern [class.base.init]
- C++20-Standard (ISO/IEC 14882:2020):
-
- 11.4.4 Konstruktoren [class.ctor]
-
- 11.10.2 Initialisierung von Basisklassen und Mitgliedern [class.base.init]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 15.1 Konstruktoren [class.ctor]
-
- 15.6.2 Initialisierung von Basisklassen und Mitgliedern [class.base.init]
- C++14-Standard (ISO/IEC 14882:2014):
-
- 12.1 Konstruktoren [class.ctor]
-
- 12.6.2 Initialisierung von Basisklassen und Mitgliedern [class.base.init]
- C++11-Standard (ISO/IEC 14882:2011):
-
- 12.1 Konstruktoren [class.ctor]
-
- 12.6.2 Initialisierung von Basisklassen und Mitgliedern [class.base.init]
- C++98-Standard (ISO/IEC 14882:1998):
-
- 12.1 Konstruktoren [class.ctor]
-
- 12.6.2 Initialisierung von Basisklassen und Mitgliedern [class.base.init]