Namespaces
Variants

Default-initialization

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

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

Inhaltsverzeichnis

Syntax

T object  ; (1)
new T (2)

Erklärung

Default-Initialisierung wird in drei Situationen durchgeführt:

1) wenn eine Variable mit automatischer, statischer oder thread-lokaler storage duration ohne Initialisierer deklariert wird;
2) wenn ein Objekt mit dynamischer Speicherdauer durch einen new-expression ohne Initialisierer erzeugt wird;
3) wenn eine Basisklasse oder ein nicht-statisches Datenelement nicht in einer Constructor Initializer List aufgeführt wird und dieser Constructor aufgerufen wird.

Die Auswirkungen der Default-Initialisierung sind:

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

Standardinitialisierung eines const-Objekts

Wenn ein Programm die Standardinitialisierung eines Objekts eines const -qualifizierten Typs T erfordert, muss T ein const-default-constructible Klassentyp oder Array davon sein.

Ein Klassentyp T ist const-default-konstruierbar, wenn die Default-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) non-POD-Klassentypen (oder Arrays davon) mit automatischer Speicherdauer wurden als default-initialized betrachtet, wenn kein Initialisierer verwendet wird. Skalare und POD-Typen mit dynamischer Speicherdauer wurden als nicht initialisiert betrachtet (seit C++11 wurde diese Situation als eine Form der Default-Initialisierung neu klassifiziert).

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

jede potenziell konstruierte Basisklasse von T ist const-standardkonstruierbar.

Unbestimmte und fehlerhafte Werte

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

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

(bis C++26)

Wenn Speicher für ein Objekt mit automatischer oder dynamischer Speicherdauer bereitgestellt wird, haben die Bytes, die den Speicher für das Objekt bilden, die folgenden Anfangswerte:

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

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

  • Wenn irgendein Bit in der Wertdarstellung einen unbestimmten Wert hat, hat das Objekt einen unbestimmten Wert .
  • Andernfalls, wenn irgendein 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 undefined .

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

(since C++26)

Sonderfälle

Die folgenden Typen sind uninitialized-friendly :

(seit C++17)
  • unsigned char
  • char , falls sein zugrundeliegender Typ unsigned char ist

Bei einem unbestimmten oder fehlerhaften (seit C++26) Wert value ist der nicht initialisierte Ergebniswert von value :

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

Wenn eine Auswertung eval einen undefinierten oder fehlerhaften (seit C++26) Wert value eines initialisierungsfreundlichen 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 eine Auswertung des rechten Operanden eines einfachen Zuweisungsoperators , dessen linker Operand ein L-Wert eines uninitialized-friendly Typs ist.
In diesem Fall wird der Wert des Objekts, auf das der linke Operand verweist, durch den nicht initialisierten Ergebniswert von value ersetzt.
  • eval ist die Auswertung des Initialisierungsausdrucks bei der Initialisierung eines Objekts eines uninitialized-friendly-Typs.
(seit C++17)
In diesem Fall wird das Objekt mit dem nicht initialisierten Ergebniswert von value initialisiert.

Die Konvertierung eines unbestimmten Werts eines uninitialisierungsfreundlichen Typs erzeugt einen unbestimmten Wert.

Die Konvertierung eines fehlerhaften Werts eines uninitialized-friendly-Typs erzeugt einen fehlerhaften Wert, das Ergebnis der Konvertierung ist der Wert des konvertierten Operanden.

(since C++26)
// Fall 1: Nicht initialisierte Objekte mit dynamischer Speicherdauer
// Alle C++-Versionen: unbestimmter Wert + undefiniertes Verhalten
int f(bool b)
{
    unsigned char* c = new unsigned char;
    unsigned char d = *c; // OK, "d" hat einen unbestimmten Wert
    int e = d;            // undefiniertes Verhalten
    return b ? d : 0;     // undefiniertes Verhalten wenn "b" true ist
}
// Fall 2: Nicht initialisierte Objekte mit automatischer Speicherdauer
// bis C++26: unbestimmter Wert + undefiniertes Verhalten
// seit C++26: fehlerhafter Wert + fehlerhaftes Verhalten
int g(bool b)
{
    unsigned char c;     // "c" hat einen unbestimmten/fehlerhaften Wert
    unsigned char d = c; // kein undefiniertes/fehlerhaftes Verhalten,
                         // aber "d" hat einen unbestimmten/fehlerhaften Wert
    assert(c == d);      // gilt, aber beide Integralpromotionen haben
                         // undefiniertes/fehlerhaftes Verhalten
    int e = d;           // undefiniertes/fehlerhaftes Verhalten
    return b ? d : 0;    // undefiniertes/fehlerhaftes Verhalten wenn "b" true ist
}
// Gleich wie Fall 2
void h()
{
    int d1, d2;  // "d1" und "d2" haben unbestimmte/fehlerhafte Werte
    int e1 = d1; // undefiniertes/fehlerhaftes Verhalten
    int e2 = d1; // undefiniertes/fehlerhaftes Verhalten
    assert(e1 == e2); // gilt
    assert(e1 == d1); // gilt, undefiniertes/fehlerhaftes Verhalten
    assert(e2 == d1); // gilt, undefiniertes/fehlerhaftes Verhalten
    // kein undefiniertes/fehlerhaftes Verhalten,
    // aber "d2" hat einen unbestimmten/fehlerhaften Wert
    std::memcpy(&d2, &d1, sizeof(int));
    assert(e1 == d2); // gilt, undefiniertes/fehlerhaftes Verhalten
    assert(e2 == d2); // gilt, undefiniertes/fehlerhaftes Verhalten
}

Hinweise

Referenzen und konstante skalare Objekte können nicht standardmäßig initialisiert werden.

Feature-Test-Makro Wert Std Feature
__cpp_constexpr 201907L (C++20) Triviale Default-Initialisierung und Asm-Deklaration in constexpr Funktionen

Beispiel

#include <string>
struct T1 { int mem; };
struct T2
{
    int mem;
    T2() {} // "mem" ist nicht in der Initialisierungsliste
};
int n; // statisch Nicht-Klasse, eine zweiphasige Initialisierung wird durchgeführt:
       // 1) Null-Initialisierung initialisiert n auf Null
       // 2) Standard-Initialisierung macht nichts, lässt n bei Null
int main()
{
    [[maybe_unused]]
    int n;            // Nicht-Klasse, der Wert ist unbestimmt
    std::string s;    // Klasse, ruft Standardkonstruktor auf, der Wert ist ""
    std::string a[2]; // Array, standardinitialisiert die Elemente, der Wert ist {"", ""}
//  int& r;           // Fehler: eine Referenz
//  const int n;      // Fehler: eine const Nicht-Klasse
//  const T1 t1;      // Fehler: const Klasse mit implizitem Standardkonstruktor
    [[maybe_unused]]
    T1 t1;            // Klasse, ruft impliziten Standardkonstruktor auf
    const T2 t2;      // const Klasse, ruft den benutzerdefinierten Standardkonstruktor auf
                      // t2.mem ist standardinitialisiert
}

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 178 C++98 Es gab keine Wertinitialisierung;
leerer Initialisierer rief Standardinitialisierung auf
(obwohl new T ( ) auch Nullinitialisierung durchführt)
leerer Initialisierer ruft
Wertinitialisierung auf
CWG 253 C++98 Standardinitialisierung eines const-Objekts konnte
keinen implizit deklarierten Standardkonstruktor aufrufen
erlaubt, wenn alle Teilobjekte initialisiert sind
CWG 616 C++98 Lvalue-zu-Rvalue-Konvertierung jedes
nicht initialisierten Objekts war immer UB
unbestimmter unsigned char ist erlaubt
CWG 1787 C++98 Lesen von einem unbestimmten unsigned char
zwischengespeichert in einem Register war UB
als wohldefiniert festgelegt

Siehe auch