Initialization
Initialisierung einer Variablen stellt ihren Anfangswert zum Zeitpunkt der Konstruktion bereit.
Der Anfangswert kann im Initialisierungsabschnitt eines Deklarators oder eines new-Ausdrucks bereitgestellt werden. Dies findet auch während Funktionsaufrufen statt: Funktionsparameter und Funktionsrückgabewerte werden ebenfalls initialisiert.
Inhaltsverzeichnis |
Initialisierer
Für jeden Deklarator kann der Initialisierer (falls vorhanden) einer der folgenden sein:
=
Ausdruck
|
(1) | ||||||||
= {}
= {
Initialisiererliste
}
= {
Designated-Initialisiererliste
}
|
(2) |
(seit C++20) |
|||||||
(
Ausdrucksliste
)
(
Initialisiererliste
)
|
(3) |
(bis C++11)
(seit C++11) |
|||||||
{}
{
Initialisiererliste
}
{
Designated-Initialisiererliste
}
|
(4) |
(seit C++11)
(seit C++11) (seit C++20) |
|||||||
| expression | - | beliebiger Ausdruck (außer nicht in Klammern gesetzte Komma-Ausdrücke ) |
| expression-list | - | eine kommagetrennte Liste von Ausdrücken (außer nicht in Klammern gesetzte Komma-Ausdrücke) |
| initializer-list | - | eine kommagetrennte Liste von Initialisierer-Klauseln (siehe unten) |
| designated-initializer-list | - | eine kommagetrennte Liste von designierten Initialisierer-Klauseln |
Eine
Initialisiererklausel
kann eines der folgenden Elemente sein:
| expression | (1) | ||||||||
{}
|
(2) | ||||||||
{
initializer-list
}
|
(3) | ||||||||
{
designated-initializer-list
}
|
(4) | (seit C++20) | |||||||
Syntaxen (2-4) werden gemeinsam als geschweifte Klammern initializer list bezeichnet.
Semantik von Initialisierern
Wenn kein Initialisierer für ein Objekt angegeben wird, wird das Objekt default-initialized . Wenn kein Initialisierer für eine reference angegeben wird, ist das Programm ill-formed.
Wenn der für ein Objekt angegebene Initialisierer ( ) ist (kann aufgrund der Syntaxeinschränkung nicht in Deklaratoren vorkommen), wird das Objekt wertinitialisiert . Wenn der für eine Referenz angegebene Initialisierer ( ) ist, ist das Programm fehlerhaft.
Die Semantik von Initialisierern ist wie folgt:
- Wenn die zu initialisierende Entität eine Referenz ist, siehe Referenzinitialisierung .
-
Andernfalls ist die zu initialisierende Entität ein Objekt. Gegeben sei der Typ des Objekts als
T:
-
- Wenn der Initialisierer von der Syntax (1) ist, wird das Objekt copy-initialized .
|
(bis C++11) |
|
(seit C++11) |
-
- Wenn der Initialisierer von der Syntax (3) ist, wird das Objekt direct-initialized .
#include <string> std::string s1; // Standardinitialisierung std::string s2(); // KEINE Initialisierung! // deklariert tatsächlich eine Funktion "s2" // ohne Parameter mit Rückgabetyp std::string std::string s3 = "hello"; // Kopierinitialisierung std::string s4("hello"); // Direktinitialisierung std::string s5{'a'}; // Listeninitialisierung (seit C++11) char a[3] = {'a', 'b'}; // Aggregatinitialisierung // (Teil der Listeninitialisierung seit C++11) char& c = a[0]; // Referenzinitialisierung
Nicht-lokale Variablen
Alle nicht-lokalen Variablen mit statischer storage duration werden als Teil des Programmstarts initialisiert, bevor die Ausführung der main function beginnt (sofern nicht verzögert, siehe unten). Alle nicht-lokalen Variablen mit thread-lokaler Speicherdauer werden als Teil des Thread-Starts initialisiert, sequenziert vor dem Beginn der Ausführung der Thread-Funktion. Für beide dieser Variablenklassen erfolgt die Initialisierung in zwei distinct Phasen:
Statische Initialisierung
Es gibt zwei Formen der statischen Initialisierung:
In der Praxis:
- Constant-Initialisierung erfolgt normalerweise zur Kompilierzeit. Vorkalkulierte Objektrepräsentationen werden als Teil des Programmabbilds gespeichert. Falls der Compiler dies nicht tut, muss er dennoch garantieren, dass die Initialisierung vor jeglicher dynamischer Initialisierung stattfindet.
-
Variablen, die Null-initialisiert werden sollen, werden im
.bss-Segment des Programmabbilds platziert, das keinen Speicherplatz auf der Festplatte belegt und beim Laden des Programms vom Betriebssystem mit Nullen gefüllt wird.
Dynamische Initialisierung
Nachdem alle statischen Initialisierungen abgeschlossen sind, erfolgt die dynamische Initialisierung nicht-lokaler Variablen in den folgenden Situationen:
|
2)
Partially-ordered dynamic initialization
, die für alle inline-Variablen gilt, die keine implizit oder explizit instanziierten Spezialisierungen sind. Wenn eine teilgeordnete V vor einer geordneten oder teilgeordneten W in jeder Übersetzungseinheit definiert wird, ist die Initialisierung von V vor der Initialisierung von W sequenziert (oder findet-vor statt, wenn das Programm einen Thread startet).
|
(since C++17) |
Wenn die Initialisierung einer nicht-lokalen Variable mit statischer oder Thread-Speicherdauer durch eine Exception beendet wird, std::terminate wird aufgerufen.
Frühe dynamische Initialisierung
Den Compilern ist es erlaubt, dynamisch initialisierte Variablen als Teil der statischen Initialisierung (im Wesentlichen zur Kompilierzeit) zu initialisieren, wenn beide folgenden Bedingungen zutreffen:
Aufgrund der obigen Regel ist es unbestimmt, wenn die Initialisierung eines Objekts
o1
auf ein namespace-globales Objekt
o2
verweist, das potenziell dynamische Initialisierung erfordert, aber später in derselben Übersetzungseinheit definiert ist, ob der verwendete Wert von
o2
der Wert des vollständig initialisierten
o2
sein wird (weil der Compiler die Initialisierung von
o2
zur Compilezeit vorgezogen hat) oder ob es der Wert des lediglich null-initialisierten
o2
sein wird.
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // nicht spezifiziert: // dynamisch auf 0.0 initialisiert, wenn d1 dynamisch initialisiert wird, oder // dynamisch auf 1.0 initialisiert, wenn d1 statisch initialisiert wird, oder // statisch auf 0.0 initialisiert (weil dies sein Wert wäre, // wenn beide Variablen dynamisch initialisiert würden) double d1 = fd(); // kann statisch oder dynamisch auf 1.0 initialisiert werden
Verzögerte dynamische Initialisierung
Es ist implementierungsdefiniert, ob die dynamische Initialisierung vor der ersten Anweisung der main-Funktion (für statische Variablen) oder der Initialisierungsfunktion des Threads (für thread-lokale Variablen) stattfindet oder auf danach verschoben wird.
Wenn die Initialisierung einer nicht-inline-Variable (seit C++17) aufgeschoben wird, um nach der ersten Anweisung der Haupt-/Thread-Funktion zu erfolgen, geschieht sie vor der ersten ODR-Nutzung einer beliebigen Variable mit statischer/Thread-Speicherdauer, die in derselben Übersetzungseinheit definiert ist wie die zu initialisierende Variable. Wenn aus einer gegebenen Übersetzungseinheit keine Variable oder Funktion ODR-genutzt wird, können die nicht-lokalen Variablen, die in dieser Übersetzungseinheit definiert sind, möglicherweise nie initialisiert werden (dies modelliert das Verhalten einer On-Demand-Dynamic-Library). Solange jedoch irgendetwas aus einer Übersetzungseinheit ODR-genutzt wird, werden alle nicht-lokalen Variablen, deren Initialisierung oder Destruktion Seiteneffekte hat, initialisiert, selbst wenn sie im Programm nicht verwendet werden.
|
Wenn die Initialisierung einer Inline-Variable verzögert wird, erfolgt sie vor der ersten ODR-Verwendung dieser spezifischen Variable. |
(since C++17) |
// ============ // == Datei 1 == #include "a.h" #include "b.h" B b; A::A() { b.Use(); } // ============ // == Datei 2 == #include "a.h" A a; // ============ // == Datei 3 == #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); } // Wenn a initialisiert wird, bevor main betreten wird, könnte b noch nicht initialisiert sein // an der Stelle, wo A::A() es verwendet (da dynamische Initialisierung über Übersetzungseinheiten hinweg // in unbestimmter Reihenfolge erfolgt) // Wenn a zu einem Zeitpunkt nach der ersten Anweisung von main initialisiert wird (was eine ODR-Nutzung // einer in Datei 1 definierten Funktion erzwingt und deren dynamische Initialisierung ausführt), // dann wird b vor seiner Verwendung in A::A initialisiert
Statische lokale Variablen
Für die Initialisierung von lokalen (d. h. Blockbereich) statischen und thread-lokalen Variablen, siehe static block variables .
Ein Initialisierer ist in einer Blockbereichsdeklaration einer Variable mit externer oder interner Verknüpfung nicht zulässig. Eine solche Deklaration muss mit extern erscheinen und kann keine Definition sein.
Klassenmitglieder
Nicht-statische Datenelemente können mit einer Member-Initialisierungsliste oder mit einem Standard-Memberinitialisierer initialisiert werden.
Hinweise
Die Reihenfolge der Zerstörung nicht-lokaler Variablen wird in std::exit beschrieben.
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 270 | C++98 |
die Reihenfolge der Initialisierung statischer Datenelemente
von Klassentemplates war nicht spezifiziert |
als ungeordnet spezifiziert, außer für
explizite Spezialisierungen und Definitionen |
| CWG 441 | C++98 |
nicht-lokale Referenzen mit statischer Speicherdauer wurden
nicht immer vor dynamischen Initialisierungen initialisiert |
als statische Initialisierung betrachtet, immer
vor dynamischen Initialisierungen initialisiert |
| CWG 1415 | C++98 |
eine Blockbereich-
extern
Variablendeklaration
könnte 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 |
es ist Teil der Initialisierung |
Siehe auch
- Copy Elision
- Konvertierungskonstruktor
- Kopierkonstruktor
- Standardkonstruktor
-
explicit - Move-Konstruktor
-
new
|
C-Dokumentation
für
Initialisierung
|