Initialisierung
Die Initialisierung einer Variablen liefert ihren Anfangswert zum Zeitpunkt der Konstruktion.
Der Anfangswert kann im Initialisierer-Teil eines Deklarators oder eines new-Ausdrucks bereitgestellt werden. Sie findet auch während von Funktionsaufrufen statt: Funktionsparameter und Funktionsrückgabewerte werden ebenfalls initialisiert.
Inhalt |
[bearbeiten] Initialisierer
Für jeden Deklarator kann der Initialisierer (falls vorhanden) einer der folgenden sein:
= Ausdruck |
(1) | ||||||||
= {}= { Initialisiererliste }= { benannte-Initialisiererliste } |
(2) | (seit C++20) | |||||||
( Ausdrucksliste )( Initialisiererliste ) |
(3) | (bis C++11) (seit C++11) | |||||||
{}{ Initialisiererliste }{ benannte-Initialisiererliste } |
(4) | (seit C++11) (seit C++11) (seit C++20) | |||||||
| expression | - | beliebiger Ausdruck (außer nicht geklammerte Komma-Ausdrücke) |
| Ausdrucksliste | - | eine durch Kommas getrennte Liste von Ausdrücken (außer nicht geklammerten Komma-Ausdrücken) |
| Initialisiererliste | - | eine durch Kommas getrennte Liste von Initialisierer-Klauseln (siehe unten) |
| benannte-Initialisiererliste | - | eine durch Kommas getrennte Liste von benannten Initialisierer-Klauseln |
Eine Initialisierer-Klausel kann eine der folgenden sein
| expression | (1) | ||||||||
{}
|
(2) | ||||||||
{ Initialisiererliste } |
(3) | ||||||||
{ benannte-Initialisiererliste } |
(4) | (seit C++20) | |||||||
Die Syntaxen (2-4) werden zusammen als in geschweifte Klammern eingeschlossene Initialisiererliste bezeichnet.
[bearbeiten] Semantik von Initialisierern
Wenn für ein Objekt kein Initialisierer angegeben ist, wird das Objekt standardinitialisiert. Wenn für eine Referenz kein Initialisierer angegeben ist, ist das Programm fehlerhaft.
Wenn der für ein Objekt angegebene Initialisierer () lautet (kann aufgrund der Syntaxbeschränkung nicht in Deklaratoren vorkommen), wird das Objekt wertinitialisiert. Wenn der für eine Referenz angegebene Initialisierer () lautet, ist das Programm fehlerhaft.
Die Semantik von Initialisierern ist wie folgt:
- Wenn das zu initialisierende Element eine Referenz ist, siehe Referenzinitialisierung.
- Andernfalls ist das zu initialisierende Element ein Objekt. Angenommen, der Typ des Objekts ist
T:
- Wenn der Initialisierer die Syntax (1) hat, wird das Objekt kopierinitialisiert.
|
(bis C++11) |
|
(seit C++11) |
- Wenn der Initialisierer die Syntax (3) hat, wird das Objekt direktinitialisiert.
#include <string> std::string s1; // default-initialization std::string s2(); // NOT an initialization! // actually declares a function “s2” // with no parameter and returns std::string std::string s3 = "hello"; // copy-initialization std::string s4("hello"); // direct-initialization std::string s5{'a'}; // list-initialization (since C++11) char a[3] = {'a', 'b'}; // aggregate initialization // (part of list initialization since C++11) char& c = a[0]; // reference initialization
[bearbeiten] Nichtlokale Variablen
Alle nichtlokalen Variablen mit statischer Speicherdauer werden als Teil des Programmstarts initialisiert, bevor die Ausführung der main-Funktion beginnt (sofern nicht verzögert, siehe unten). Alle nichtlokalen Variablen mit Thread-lokaler Speicherdauer werden als Teil des Thread-Starts initialisiert, vor der Ausführung der Thread-Funktion. Für beide Arten von Variablen erfolgt die Initialisierung in zwei verschiedenen Phasen:
[bearbeiten] Statische Initialisierung
Es gibt zwei Formen der statischen Initialisierung:
In der Praxis:
- Die Konstanteninitialisierung wird normalerweise zur Kompilierzeit durchgeführt. Vorkompilierte Objekt-Darstellungen werden als Teil des Programm-Images gespeichert. Wenn der Compiler dies nicht tut, muss er dennoch garantieren, dass die Initialisierung vor jeder dynamischen Initialisierung erfolgt.
- Variablen, die nullinitialisiert werden sollen, werden im Segment
.bssdes Programm-Images platziert, das auf der Festplatte keinen Platz einnimmt und beim Laden des Programms vom Betriebssystem auf Null gesetzt wird.
[bearbeiten] Dynamische Initialisierung
Nach Abschluss aller statischen Initialisierungen erfolgt die dynamische Initialisierung von nichtlokalen Variablen in den folgenden Situationen:
|
2) Teilweise geordnete dynamische Initialisierung, die für alle Inline-Variablen gilt, die keine implizit oder explizit instanziierte Spezialisierung sind. Wenn eine teilweise geordnete V vor einer geordneten oder teilweise geordneten W in jeder Translation Unit definiert ist, ist die Initialisierung von V sequenziert vor der Initialisierung von W (oder happens-before, wenn das Programm einen Thread startet).
|
(seit C++17) |
Wenn die Initialisierung einer nichtlokalen Variablen mit statischer oder Thread-Speicherdauer über eine Ausnahme beendet wird, wird std::terminate aufgerufen.
[bearbeiten] Frühe dynamische Initialisierung
Die Compiler dürfen dynamisch initialisierte Variablen als Teil der statischen Initialisierung (im Wesentlichen zur Kompilierzeit) initialisieren, wenn beide folgenden Bedingungen erfüllt sind:
Aufgrund der obigen Regel ist es, wenn die Initialisierung eines Objekts o1 sich auf ein Objekt o2 im Namespace-Bereich bezieht, das möglicherweise eine dynamische Initialisierung erfordert, aber später in derselben Translation Unit definiert ist, undefiniert, ob der verwendete Wert von o2 der Wert des vollständig initialisierten o2 ist (weil der Compiler die Initialisierung von o2 zur Kompilierzeit befördert hat) oder der Wert von o2, der nur nullinitialisiert wurde.
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // unspecified: // dynamically initialized to 0.0 if d1 is dynamically initialized, or // dynamically initialized to 1.0 if d1 is statically initialized, or // statically initialized to 0.0 (because that would be its value // if both variables were dynamically initialized) double d1 = fd(); // may be initialized statically or dynamically to 1.0
[bearbeiten] Verzögerte dynamische Initialisierung
Es ist implementierungsabhängig, ob die dynamische Initialisierung vor der ersten Anweisung der main-Funktion (für Statics) oder der ersten Funktion des Threads (für Thread-Locals) stattfindet oder ob sie verzögert wird, um danach stattzufinden.
Wenn die Initialisierung einer nicht-inline-Variablen(seit C++17) auf nach die erste Anweisung der main/Thread-Funktion verschoben wird, geschieht dies vor der ersten ODR-Verwendung jeder Variablen mit statischer/Thread-Speicherdauer, die in derselben Translation Unit wie die zu initialisierende Variable definiert ist. Wenn keine Variable oder Funktion aus einer gegebenen Translation Unit ODR-verwendet wird, werden die in dieser Translation Unit definierten nichtlokalen Variablen möglicherweise nie initialisiert (dies modelliert das Verhalten einer On-Demand-Dynamikbibliothek). Solange jedoch etwas aus einer Translation Unit ODR-verwendet wird, werden alle nichtlokalen Variablen, deren Initialisierung oder Zerstörung Seiteneffekte hat, initialisiert, auch wenn sie im Programm nicht verwendet werden.
|
Wenn die Initialisierung einer Inline-Variable verzögert wird, geschieht dies vor der ersten ODR-Verwendung dieser spezifischen Variablen. |
(seit C++17) |
// ============ // == File 1 == #include "a.h" #include "b.h" B b; A::A() { b.Use(); } // ============ // == File 2 == #include "a.h" A a; // ============ // == File 3 == #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); } // If a is initialized before main is entered, b may still be uninitialized // at the point where A::A() uses it (because dynamic initialization is // indeterminately sequenced across translation units) // If a is initialized at some point after the first statement of main (which odr-uses // a function defined in File 1, forcing its dynamic initialization to run), // then b will be initialized prior to its use in A::A
[bearbeiten] Statische lokale Variablen
Für die Initialisierung von lokalen (d.h. Block-Scope) statischen und Thread-lokalen Variablen siehe statische Blockvariablen.
Ein Initialisierer ist in einer Deklaration auf Block-Ebene einer Variablen mit externer oder interner Bindung nicht erlaubt. Eine solche Deklaration muss mit extern erscheinen und darf keine Definition sein.
[bearbeiten] Klassenmitglieder
Nicht-statische Datenmitglieder können mit einer Member-Initialisierungsliste oder mit einem Standard-Member-Initialisierer initialisiert werden.
[bearbeiten] Anmerkungen
Die Reihenfolge der Zerstörung von nichtlokalen Variablen wird in std::exit beschrieben.
[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 270 | C++98 | Die Reihenfolge der Initialisierung von statischen Datenmitgliedern von Klassenvorlagen war spezifiziert |
wurde als ungeordnet spezifiziert, außer für explizite Spezialisierungen und Definitionen |
| CWG 441 | C++98 | Nichtlokale Referenzen mit statischer Speicherdauer waren nicht immer vor dynamischen Initialisierungen |
als statische Initialisierung betrachtet, immer vor dynamischen Initialisierungen initialisiert |
| CWG 1415 | C++98 | Eine Deklaration einer extern-Variable im Block-Scope konnte eine Definition sein |
verboten (kein Initialisierer in solchen Deklarationen erlaubt) |
| CWG 2599 | C++98 | Es war unklar, ob die Auswertung von Funktionsargumenten im Initialisierer Teil der Initialisierung ist |
sie ist Teil der Initialisierung |
[bearbeiten] Siehe auch
- Kopier-Eliminierung
- Konstruktor zum Konvertieren
- Kopierkonstruktor
- Standardkonstruktor
-
explicit - Verschiebekonstruktor
-
new
| C-Dokumentation für Initialisierung
|