Default-initialization
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:
Die Auswirkungen der Default-Initialisierung sind:
-
wenn
Tein (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
Tein Array-Typ ist, wird jedes Element des Arrays standardinitialisiert; - andernfalls wird keine Initialisierung durchgeführt (siehe Anmerkungen ).
|
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) |
|
(bis C++11) |
|
(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 keine Initialisierung für ein Objekt (einschließlich Unterobjekten ) durchgeführt wird, behält ein solches Byte seinen Anfangswert, bis dieser Wert ersetzt wird.
|
(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.
|
(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:
-
- Der zweite oder dritte Operand eines Bedingungsausdrucks .
- Der rechte Operand eines Kommaausdrucks .
-
Der Operand einer
integralen Konvertierung
,
expliziten Typumwandlung
oder
static_castzu einem uninitialisierungsfreundlichen Typ. - Ein verworfen-wertiger Ausdruck .
- 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 |