Namensräume
Varianten
Aktionen

Standardinitialisierung

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
 
 

Dies ist die Initialisierung, die durchgeführt wird, wenn ein Objekt ohne Initialisierer konstruiert wird.

Inhalt

[bearbeiten] Syntax

T objekt ; (1)
new T (2)

[bearbeiten] Erklärung

Die Standard-Initialisierung wird in drei Situationen durchgeführt:

1) wenn eine Variable mit automatischer, statischer oder Thread-lokaler Speicherdauer ohne Initialisierer deklariert wird;
2) wenn ein Objekt mit dynamischer Speicherdauer durch einen new-Ausdruck ohne Initialisierer erstellt wird;
3) wenn eine Basisklasse oder ein nicht-statisches Datenelement in einer Konstruktor-Initialisierungsliste nicht erwähnt wird und dieser Konstruktor aufgerufen wird.

Die Auswirkungen der Standard-Initialisierung sind:

  • wenn T ein (möglicherweise cv-qualifizierter) Nicht-POD(bis C++11) Klassentyp ist, werden die Konstruktoren betrachtet und der Überladungsauflösung mit einer leeren Argumentliste unterzogen. Der ausgewählte Konstruktor (einer der Standardkonstruktoren) wird aufgerufen, um den Anfangswert für das neue Objekt bereitzustellen;
  • wenn T ein Array-Typ ist, wird jedes Element des Arrays standardmäßig initialisiert;
  • andernfalls wird keine Initialisierung durchgeführt (siehe Anmerkungen).

[bearbeiten] Standard-Initialisierung eines const-Objekts

Wenn ein Programm die Standard-Initialisierung eines Objekts vom const-qualifizierten Typ T verlangt, muss T ein const-standardkonstruierbarer Klassentyp oder ein Array davon sein.

Ein Klassentyp T ist const-standardkonstruierbar, wenn die Standard-Initialisierung von T einen benutzerdefinierten Konstruktor von T aufrufen würde (nicht von einer Basisklasse geerbt)(seit C++11) oder wenn

Nur (möglicherweise cv-qualifizierte) Nicht-POD-Klassentypen (oder deren Arrays) mit automatischer Speicherdauer wurden bei fehlendem Initialisierer als standardmäßig initialisiert betrachtet. Skalare und POD-Typen mit dynamischer Speicherdauer wurden als nicht initialisiert betrachtet (seit C++11 wurde diese Situation als eine Form der Standard-Initialisierung neu klassifiziert).

(bis C++11)
  • jedes direkte nicht-statische Datenelement M von T vom Klassentyp X ist (oder ein Array davon), X const-standardkonstruierbar ist und
  • T keine direkten Variantenmember hat und
(bis C++11)
  • jedes direkte nicht-variante nicht-statische Datenelement M von T einen Standard-Member-Initialisierer hat oder, wenn M vom Klassentyp X ist (oder ein Array davon), X const-standardkonstruierbar ist,
  • wenn T eine Union mit mindestens einem nicht-statischen Datenelement ist, genau ein Variantenmember einen Standard-Member-Initialisierer hat,
  • wenn T keine Union ist, für jedes anonyme Union-Mitglied mit mindestens einem nicht-statischen Datenelement (falls vorhanden), genau ein nicht-statisches Datenelement einen Standard-Member-Initialisierer hat, und
(seit C++11)

jede potenziell konstruierte Basisklasse von T const-standardkonstruierbar ist.

[bearbeiten] Unbestimmte und fehlerhafte Werte

Wenn Speicher für ein Objekt mit automatischer oder dynamischer Speicherdauer bereitgestellt wird, hat das Objekt einen unbestimmten Wert.

Wenn für ein Objekt keine Initialisierung durchgeführt wird, behält dieses Objekt einen unbestimmten Wert bei, bis dieser Wert ersetzt wird.

(bis C++26)

Wenn Speicher für ein Objekt mit automatischer oder dynamischer Speicherdauer bereitgestellt wird, haben die Bytes, aus denen der Speicher für das Objekt besteht, den folgenden Anfangswert:

  • Wenn das Objekt eine dynamische Speicherdauer hat oder das Objekt ist, das mit einer Variablen oder einem Funktionsparameter verbunden ist, dessen erste Deklaration mit [[indeterminate]] markiert ist, haben die Bytes unbestimmte Werte.
  • Andernfalls haben die Bytes fehlerhafte Werte, wobei jeder Wert unabhängig vom Zustand des Programms von der Implementierung bestimmt wird.

Wenn für ein Objekt keine Initialisierung durchgeführt wird (einschließlich Teilobjekten), behält ein solches Byte seinen Anfangswert bei, bis dieser Wert ersetzt wird.

  • Wenn ein Bit in der Wertdarstellung einen unbestimmten Wert hat, hat das Objekt einen unbestimmten Wert.
  • Andernfalls, wenn ein Bit in der Wertdarstellung einen fehlerhaften Wert hat, hat das Objekt einen fehlerhaften Wert.
(seit C++26)

Wenn eine Auswertung einen unbestimmten Wert erzeugt, ist das Verhalten undefiniert.

Wenn eine Auswertung einen fehlerhaften Wert erzeugt, ist das Verhalten fehlerhaft.

(seit C++26)

[bearbeiten] Sonderfälle

Die folgenden Typen sind uninitialisiertfreundlich:

(seit C++17)
  • unsigned char
  • char, wenn sein zugrunde liegender Typ unsigned char ist

Gegeben sei ein unbestimmter oder fehlerhafter(seit C++26) Wert value, das uninitialisierte Ergebnis von value ist:

  • Ein unbestimmter Wert, wenn value ebenfalls ein unbestimmter Wert ist.
  • value, wenn value ein fehlerhafter Wert ist.
(seit C++26)

Wenn eine Auswertung eval einen unbestimmten oder fehlerhaften(seit C++26) Wert value eines uninitialisiertfreundlichen Typs erzeugt, ist das Verhalten in den folgenden Fällen wohldefiniert:

  • eval ist die Auswertung eines der folgenden Ausdrücke und Operanden:
In diesem Fall ist das Ergebnis der Operation der uninitialisierte Ergebniswert von value.
  • eval ist die Auswertung des rechten Operanden eines einfachen Zuweisungsoperators, dessen linker Operand ein lvalue eines uninitialisiertfreundlichen Typs ist.
In diesem Fall wird der Wert des Objekts, auf das der linke Operand verweist, durch den uninitialisierten Ergebniswert von value ersetzt.
  • eval ist die Auswertung des Initialisierungsdrucks bei der Initialisierung eines Objekts eines uninitialisiertfreundlichen Typs.
  • value darf nicht vom Typ std::byte sein, wenn das zu initialisierende Objekt nicht vom Typ std::byte ist.
(seit C++17)
In diesem Fall wird dieses Objekt mit dem uninitialisierten Ergebniswert von value initialisiert.

Das Umwandeln eines unbestimmten Wertes eines uninitialisiertfreundlichen Typs erzeugt einen unbestimmten Wert.

Das Umwandeln eines fehlerhaften Wertes eines uninitialisiertfreundlichen Typs erzeugt einen fehlerhaften Wert; das Ergebnis der Umwandlung ist der Wert des umgewandelten Operanden.

(seit C++26)
// Case 1: Uninitialized objects with dynamic storage duration
// All C++ versions: indeterminate value + undefined behavior
int f(bool b)
{
    unsigned char* c = new unsigned char;
    unsigned char d = *c; // OK, “d” has an indeterminate value
    int e = d;            // undefined behavior
    return b ? d : 0;     // undefined behavior if “b” is true
}
 
// Case 2: Uninitialized objects with automatic storage duration
// until C++26: indeterminate value + undefined behavior
// since C++26: erroneous value + erroneous behavior
int g(bool b)
{
    unsigned char c;     // “c” has an indeterminate/erroneous value
 
    unsigned char d = c; // no undefined/erroneous behavior,
                         // but “d” has an indeterminate/erroneous value
 
    assert(c == d);      // holds, but both integral promotions have
                         // undefined/erroneous behavior
 
    int e = d;           // undefined/erroneous behavior
    return b ? d : 0;    // undefined/erroneous behavior if “b” is true
}
 
// Same as case 2
void h()
{
    int d1, d2;  // “d1” and “d2” have indeterminate/erroneous values
    int e1 = d1; // undefined/erroneous behavior
    int e2 = d1; // undefined/erroneous behavior
 
    assert(e1 == e2); // holds
    assert(e1 == d1); // holds, undefined/erroneous behavior
    assert(e2 == d1); // holds, undefined/erroneous behavior
 
    // no undefined/erroneous behavior,
    // but “d2” has an indeterminate/erroneous value
    std::memcpy(&d2, &d1, sizeof(int));
 
    assert(e1 == d2); // holds, undefined/erroneous behavior
    assert(e2 == d2); // holds, undefined/erroneous behavior
}

[bearbeiten] Anmerkungen

Referenzen und const-Skalarobjekte können nicht standardmäßig initialisiert werden.

Feature-Testmakro Wert Std Feature
__cpp_constexpr 201907L (C++20) Triviale Standard-Initialisierung und asm-Deklaration in constexpr Funktionen.

[bearbeiten] Beispiel

#include <string>
 
struct T1 { int mem; };
 
struct T2
{
    int mem;
    T2() {} // “mem” is not in the initializer list
};
 
int n; // static non-class, a two-phase initialization is done:
       // 1) zero-initialization initializes n to zero
       // 2) default-initialization does nothing, leaving n being zero
 
int main()
{
    [[maybe_unused]]
    int n;            // non-class, the value is indeterminate
    std::string s;    // class, calls default constructor, the value is ""
    std::string a[2]; // array, default-initializes the elements, the value is {"", ""}
//  int& r;           // Error: a reference
//  const int n;      // Error: a const non-class
//  const T1 t1;      // Error: const class with implicit default constructor
    [[maybe_unused]]
    T1 t1;            // class, calls implicit default constructor
    const T2 t2;      // const class, calls the user-provided default constructor
                      // t2.mem is default-initialized
}

[bearbeiten] Defect reports

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 178 C++98 Es gab keine Wertinitialisierung;
Ein leerer Initialisierer rief die Standard-Initialisierung auf.
(obwohl new T() auch eine Null-Initialisierung durchführt)
Ein leerer Initialisierer ruft auf:
Wertinitialisierung
CWG 253 C++98 Die Standard-Initialisierung eines const-Objekts konnte
einen implizit deklarierten Standardkonstruktor aufrufen.
Erlaubt, wenn alle Teilobjekte initialisiert sind.
CWG 616 C++98 Konvertierung von lvalue zu rvalue von jedem
uninitialisierten Objekt war immer UB.
Ein unbestimmtes unsigned char ist erlaubt.
CWG 1787 C++98 Das Lesen von einem unbestimmten unsigned char
in einem Register zwischengespeichert war UB.
wurde wohlformuliert

[bearbeiten] Siehe auch