Standardinitialisierung
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:
Die Auswirkungen der Standard-Initialisierung sind:
- wenn
Tein (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
Tein 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) |
|
(bis C++11) |
|
(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 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.
|
(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.
|
(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:
- Der zweite oder dritte Operand eines bedingten Ausdrucks.
- Der rechte Operand eines Komma-Ausdrucks.
- Der Operand einer Ganzzahlkonvertierung, einer expliziten Umwandlung oder eines
static_castin einen uninitialisiertfreundlichen Typ. - Ein Ausdruck mit verworfenem Wert.
- 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.
| (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 |