Namespaces
Variants

Constructors and member initializer lists

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 -

Dynamische Ausnahmespezifikation

(bis C++11)

entweder Dynamische Ausnahmespezifikation
oder noexcept-Spezifikation

(seit C++11)
(bis C++17)

noexcept-Spezifikation

(seit C++17)
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:

  • 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)
1) Initialisiert direkt das Datenelement mit dem Namen member mit initializer . member kann nur nicht-statische Datenelemente benennen.
2) Initialisiert ein Klassenobjekt mit initializer . class kann nur die folgenden Klassen benennen:
(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.
3) Initialisiert mehrere Basisklassen-Subobjekte unter Verwendung einer Pack-Erweiterung .
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 Konstruktor

Wenn 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 Konstruktoren

Siehe 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:

1) Wenn der Konstruktor für die am stärksten abgeleitete Klasse ist, werden virtuelle Basisklassen in der Reihenfolge initialisiert, in der sie in der Tiefensuche von links nach rechts in den Basisklassendeklarationen erscheinen (links nach rechts bezieht sich auf das Erscheinen in Basis-Spezifizierer-Listen).
2) Danach werden direkte Basisklassen in der Reihenfolge von links nach rechts initialisiert, wie sie in der Basis-Spezifiziererliste dieser Klasse erscheinen.
3) Danach werden nicht-statische Datenelemente in der Reihenfolge ihrer Deklaration in der Klassendefinition initialisiert.
4) Schließlich wird der Rumpf des Konstruktors ausgeführt.

(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]

Siehe auch