Namensräume
Varianten
Aktionen

Konstruktoren und Member-Initialisierungslisten

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Konstruktoren sind nicht-statische Memberfunktionen, die mit einer speziellen Deklaratorsyntax deklariert werden. Sie dienen zur Initialisierung von Objekten ihrer Klassentypen.

Ein Konstruktor darf keine Coroutine sein.

(seit C++20)

Ein Konstruktor darf keinen expliziten Objektparameter haben.

(seit C++23)

Inhalt

[bearbeiten] Syntax

Konstruktoren werden mithilfe von Member-Funktionsdeklaratoren der folgenden Form deklariert:

klassen-name ( parameterliste (optional) ) ausnahme (optional) attr (optional)
Klassenname - ein Primärausdruck, möglicherweise gefolgt von einer Liste von Attributen und(seit C++11) möglicherweise eingeschlossen von einem Klammernpaar.
parameter-liste - Parameterliste
except -

dynamische Ausnahme-Spezifikationen

(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 erlaubten Spezifizierer in den Deklarationsspezifizierern einer Konstruktordeklaration sind friend, inline, constexpr(seit C++11), consteval(seit C++20) und explicit (insbesondere ist kein Rückgabetyp erlaubt). Beachten Sie, dass auch cv- und Ref-Qualifizierer nicht erlaubt sind: die const- und volatile-Semantik eines unter Konstruktion stehenden Objekts tritt erst nach Abschluss des am weitesten abgeleiteten Konstruktors in Kraft.

Der Bezeichnerausdruck von klassen-name muss eine der folgenden Formen haben:

  • Für Klassen ist der Bezeichnerausdruck der eingeschleuste Klassenname der unmittelbar umschließenden Klasse.
  • Für Klassenvorlagen ist der Bezeichnerausdruck ein Klassenname, der die aktuelle Instanziierung benennt(bis C++20)der eingeschleuste Klassenname(seit C++20) der unmittelbar umschließenden Klassenvorlage.
  • Andernfalls ist der Bezeichnerausdruck ein qualifizierter Bezeichner, dessen letzter nicht-qualifizierter Bezeichner der eingeschleuste Klassenname seines Lookup-Kontextes ist.

[bearbeiten] Member-Initialisierungsliste

Der Körper einer Funktionsdefinition eines jeden Konstruktors kann, vor der öffnenden geschweiften Klammer der Verbundanweisung, die Member-Initialisierungsliste enthalten, deren Syntax das Doppelpunktzeichen : gefolgt von der durch Kommas getrennten Liste von einem oder mehreren member-initializer  ist, von denen jeder die folgende Syntax hat:

klasse-oder-bezeichner ( ausdrucksliste (optional) ) (1)
klasse-oder-bezeichner geschweifte-initialisierungsliste (2) (seit C++11)
parameter-pack ... (3) (seit C++11)
1) Initialisiert die Basis oder das Member, benannt durch klasse-oder-bezeichner, unter Verwendung von direkter Initialisierung oder, falls ausdrucksliste leer ist, Wertinitialisierung.
2) Initialisiert die Basis oder das Member, benannt durch klasse-oder-bezeichner, unter Verwendung von Listeninitialisierung (was zur Wertinitialisierung wird, wenn die Liste leer ist, und zur Aggregatinitialisierung bei der Initialisierung eines Aggregats).
3) Initialisiert mehrere Basen unter Verwendung einer Pack-Expansion.
klasse-oder-bezeichner - Jeder Bezeichner, der ein nicht-statisches Datenmember benennt, oder jeder Typname, der entweder die Klasse selbst (für delegierende Konstruktoren) oder eine direkte oder virtuelle Basis benennt.
Ausdrucksliste - Möglicherweise leere, durch Kommas getrennte Liste der Argumente, die an den Konstruktor der Basis oder des Members übergeben werden.
geschweifte-initialisierungsliste - In geschweiften Klammern eingeschlossene Initialisierungsliste.
parameter-pack - Name eines Variadic-Template-Parameter-Packs.
struct S
{
    int n;
 
    S(int);       // constructor declaration
 
    S() : n(7) {} // constructor definition:
                  // ": n(7)" is the initializer list
                  // ": n(7) {}" is the function body
};
 
S::S(int x) : n{x} {} // constructor definition: ": n{x}" is the initializer list
 
int main()
{
    S s;      // calls S::S()
    S s2(10); // calls S::S(int)
}

[bearbeiten] Erklärung

Konstruktoren haben keine Namen und können nicht direkt aufgerufen werden. Sie werden aufgerufen, wenn die Initialisierung stattfindet, und sie werden nach den Regeln der Initialisierung ausgewählt. Konstruktoren ohne den explicit-Spezifizierer sind Konvertierungskonstruktoren. Konstruktoren mit einem constexpr-Spezifizierer machen ihren Typ zu einem Literal-Typ. Konstruktoren, die ohne Argumente aufgerufen werden können, sind Standardkonstruktoren. Konstruktoren, die ein anderes Objekt desselben Typs als Argument nehmen, sind Kopierkonstruktoren und Verschiebungskonstruktoren.

Bevor die Verbundanweisung, die den Funktionsrumpf des Konstruktors bildet, zu laufen beginnt, ist die Initialisierung aller direkten Basen, virtuellen Basen und nicht-statischen Datenmember abgeschlossen. Die Member-Initialisierungsliste ist der Ort, an dem die Nicht-Standard-Initialisierung dieser Unterobjekte angegeben werden kann. Für Basen, die nicht standardmäßig initialisiert werden können, und für nicht-statische Datenmember, die nicht durch Standardinitialisierung oder durch ihren Standard-Member-Initialisierer, falls vorhanden, initialisiert werden können(seit C++11), wie z.B. Member von Referenz- und const-qualifizierten Typen, müssen Member-Initialisierer angegeben werden. (Beachten Sie, dass Standard-Member-Initialisierer für nicht-statische Datenmember von Klassenvorlageninstanziierungen ungültig sein können, wenn der Member-Typ oder der Initialisierer von der Vorlage abhängt.)(seit C++11) Für anonyme Unions oder variant members, die keinen Member-Initialisierer oder Standard-Member-Initialisierer(seit C++11) haben, erfolgt keine Initialisierung.

Die Initialisierer, bei denen klasse-oder-bezeichner eine virtuelle Basisklasse benennt, werden bei der Konstruktion jeder Klasse, die nicht die am weitesten abgeleitete Klasse des zu konstruierenden Objekts ist, ignoriert.

Namen, die in ausdrucksliste oder geschweifte-initialisierungsliste erscheinen, werden im Geltungsbereich des Konstruktors ausgewertet.

class X
{
    int a, b, i, j;
public:
    const int& r;
    X(int i)
      : r(a) // initializes X::r to refer to X::a
      , b{i} // initializes X::b to the value of the parameter i
      , i(i) // initializes X::i to the value of the parameter i
      , j(this->i) // initializes X::j to the value of X::i
    {}
};

Ausnahmen, die von Member-Initialisierern geworfen werden, können von einem Funktions-try-Block behandelt werden.

Wenn ein nicht-statisches Datenmember einen Standard-Member-Initialisierer hat und auch in einer Member-Initialisierungsliste vorkommt, dann 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
};
(seit C++11)

Referenz-Member können in einer Member-Initialisierungsliste nicht an temporäre Objekte gebunden werden.

struct A
{
    A() : v(42) {} // Error
    const int& v;
};

Hinweis: dasselbe gilt für Standard-Member-Initialisierer.

[bearbeiten] Operationen während Konstruktion und Zerstörung

Memberfunktionen (einschließlich virtuellen Memberfunktionen) können für ein Objekt unter Konstruktion oder Zerstörung aufgerufen werden. Ebenso kann ein Objekt unter Konstruktion oder Zerstörung 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 einer Member-Initialisierungsliste, bevor alle member-initializer  für Basisklassen abgeschlossen wurden.

Delegierender Konstruktor

Wenn der Name der Klasse selbst als klasse-oder-bezeichner in der Member-Initialisierungsliste erscheint, muss die Liste aus diesem einen Member-Initialisierer bestehen; ein solcher Konstruktor wird als delegierender Konstruktor bezeichnet, und der durch den einzigen Member 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 Kontrolle zum delegierenden Konstruktor zurück und dessen Körper wird ausgeführt.

Delegierende Konstruktoren dürfen nicht rekursiv sein.

class Foo
{
public: 
    Foo(char x, int y) {}
    Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int)
};

Erbende Konstruktoren

Siehe using-Deklaration.

(seit C++11)

[bearbeiten] Initialisierungsreihenfolge

Die Reihenfolge der Member-Initialisierer in der Liste ist unerheblich: die tatsächliche Reihenfolge der Initialisierung ist wie folgt:

1) Wenn der Konstruktor für die am weitesten abgeleitete Klasse ist, werden virtuelle Basen in der Reihenfolge initialisiert, in der sie bei einer Tiefen-Zuerst-Links-nach-Rechts-Durchwanderung der Basisklassendeklarationen erscheinen (Links-nach-Rechts bezieht sich auf das Erscheinen in den Basis-Spezifikatorlisten).
2) Dann werden direkte Basen in Links-nach-Rechts-Reihenfolge initialisiert, wie sie in der Basis-Spezifikatorliste dieser Klasse erscheinen.
3) Dann werden nicht-statische Datenmember in der Reihenfolge ihrer Deklaration in der Klassendefinition initialisiert.
4) Schließlich wird der Körper 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 Reihenfolge der Konstruktion ist.)

[bearbeiten] Anmerkungen

Feature-Testmakro Wert Std Feature
__cpp_delegating_constructors 200604L (C++11) Delegierende Konstruktoren

[bearbeiten] 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}, // initialize base class
        x(x),     // x (member) is initialized with x (parameter)
        y{0},     // y initialized to 0
        f{"test.cc", std::ios::app}, // this takes place after m and lg are initialized
        s(__func__), // __func__ is available because init-list is a part of constructor
        lg(m),    // lg uses m, which is already initialized
        m{}       // m is initialized before lg even though it appears last here
    {}            // empty compound statement
 
    Class(double a) : y(a + 1),
        x(y), // x will be initialized before y, its value here is indeterminate
        lg(m)
    {} // base class initializer does not appear in the list, it is
       // default-initialized (not the same as if Base() were used, which is value-init)
 
    Class()
    try // function try block begins before the function body, which includes init list
      : Class(0.0) // delegate constructor
    {
        // ...
    }
    catch (...)
    {
        // exception occurred on initialization
    }
};
 
int main()
{
    Class c;
    Class c1(1);
    Class c2(0.1);
}

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 194 C++98 Die Deklaratorsyntax von Konstruktoren erlaubte nur
höchstens einen Funktionsspezifizierer (z.B. ein Konstruktor
konnte nicht als inline explicit deklariert werden)
Mehrere Funktions-
spezifizierer erlaubt
CWG 257 C++98 Es war nicht spezifiziert, ob eine abstrakte Klasse
Member-Initialisierer für ihre virtuellen Basisklassen
bereitstellen sollte
Spezifiziert als nicht erforderlich
und solche Member-Initialisierer
werden bei der Ausführung ignoriert C++98 CWG 263
Die Deklaratorsyntax von Konstruktoren
verbot Konstruktoren, Freunde zu sein
Konstruktoren erlaubt, Freunde zu sein
CWG 1345 C++98 Anonyme Union-Member ohne Standard-
Member-Initialisierer wurden standardmäßig initialisiert
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
Funktionsdeklaratorsyntax
CWG 1696 C++98 Referenz-Member konnten mit temporären Objekten initialisiert werden
(deren Lebensdauer am Ende des Konstruktors enden würde)
Solche Initialisierungen
sind fehlerhaft

[bearbeiten] Referenzen

  • C++23 Standard (ISO/IEC 14882:2024)
  • 11.4.5 Konstruktoren [class.ctor]
  • 11.9.3 Initialisieren von Basen und Membern [class.base.init]
  • C++20 Standard (ISO/IEC 14882:2020)
  • 11.4.4 Konstruktoren [class.ctor]
  • 11.10.2 Initialisieren von Basen und Membern [class.base.init]
  • C++17 Standard (ISO/IEC 14882:2017)
  • 15.1 Konstruktoren [class.ctor]
  • 15.6.2 Initialisieren von Basen und Membern [class.base.init]
  • C++14 Standard (ISO/IEC 14882:2014)
  • 12.1 Konstruktoren [class.ctor]
  • 12.6.2 Initialisieren von Basen und Membern [class.base.init]
  • C++11 Standard (ISO/IEC 14882:2011)
  • 12.1 Konstruktoren [class.ctor]
  • 12.6.2 Initialisieren von Basen und Membern [class.base.init]
  • C++98 Standard (ISO/IEC 14882:1998)
  • 12.1 Konstruktoren [class.ctor]
  • 12.6.2 Initialisieren von Basen und Membern [class.base.init]

[bearbeiten] Siehe auch