Namespaces
Variants

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

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)
1) Copy-Initialisierungssyntax.
2) Aggregate-Initialisierungssyntax. (bis C++11) Listeninitialisierungssyntax. (seit C++11)
3) Direct-initialization Syntax.
4) Listeninitialisierungssyntax.
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 der Syntax (2) entspricht:
(bis C++11)
(seit C++11)
#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:

1) Wenn möglich wird constant initialization angewendet.
2) Andernfalls werden nicht-lokale static- und thread-lokale Variablen zero-initialized .

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:

1) Ungeordnete dynamische Initialisierung , die nur für (static/thread-lokale) Klassen-Template static data members und variable templates (since C++14) gilt, die nicht explicitly specialized sind. Die Initialisierung solcher static Variablen ist in unbestimmter Reihenfolge bezüglich aller anderen dynamischen Initialisierungen außer wenn das Programm einen Thread startet bevor eine Variable initialisiert wird, in welchem Fall ihre Initialisierung ungeordnet ist (since C++17) . Die Initialisierung solcher thread-local Variablen ist ungeordnet bezüglich aller anderen dynamischen Initialisierung.
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)
3) Geordnete dynamische Initialisierung , die für alle anderen nicht-lokalen Variablen gilt: Innerhalb einer einzelnen Übersetzungseinheit ist die Initialisierung dieser Variablen stets sequenziert in der exakten Reihenfolge, in der ihre Definitionen im Quellcode erscheinen. Die Initialisierung statischer Variablen in verschiedenen Übersetzungseinheiten ist unbestimmt sequenziert. Die Initialisierung thread-lokaler Variablen in verschiedenen Übersetzungseinheiten ist nicht sequenziert.

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:

1) die dynamische Version der Initialisierung ändert nicht den Wert eines anderen Objekts im Namensbereich vor dessen Initialisierung
2) die statische Version der Initialisierung erzeugt denselben Wert in der initialisierten Variable, wie durch die dynamische Initialisierung erzeugt würde, wenn alle Variablen, die nicht statisch initialisiert werden müssen, dynamisch initialisiert würden.

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

C-Dokumentation für Initialisierung