Namensräume
Varianten
Aktionen

Initialisierung

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

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)
1) Syntax der Kopierinitialisierung.
2) Syntax der Aggregatinitialisierung.(bis C++11)Syntax der Listeninitialisierung.(seit C++11)
3) Syntax der direkten Initialisierung.
4) Syntax der Listeninitialisierung.
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 (2) hat:
(bis C++11)
(seit C++11)
#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:

1) Wenn möglich, wird die Konstanteninitialisierung angewendet.
2) Andernfalls werden nichtlokale statische und Thread-lokale Variablen nullinitialisiert.

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 .bss des 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:

1) Ungeordnete dynamische Initialisierung, die nur für (statische/Thread-lokale) Klassen-Template-statische Datenmitglieder und Variablen-Templates(seit C++14) gilt, die nicht explizit spezialisiert sind. Die Initialisierung solcher statischen Variablen ist indeterminiert sequenziert im Verhältnis zu allen anderen dynamischen Initialisierungen es sei denn, das Programm startet einen Thread, bevor eine Variable initialisiert wurde, in welchem Fall ihre Initialisierung unsequenziert ist(seit C++17). Die Initialisierung solcher Thread-lokaler Variablen ist unsequenziert im Verhältnis zu allen anderen dynamischen Initialisierungen.
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)
3) Geordnete dynamische Initialisierung, die für alle anderen nichtlokalen Variablen gilt: Innerhalb einer einzelnen Translation Unit ist die Initialisierung dieser Variablen immer sequenziert in der exakten Reihenfolge ihres Erscheinens im Quellcode. Die Initialisierung statischer Variablen in verschiedenen Translation Units ist indeterminierungssequenziert. Die Initialisierung von Thread-lokalen Variablen in verschiedenen Translation Units ist unsequenziert.

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:

1) Die dynamische Version der Initialisierung ändert den Wert eines anderen Objekts im Namespace-Bereich vor dessen Initialisierung nicht.
2) Die statische Version der Initialisierung erzeugt denselben Wert in der initialisierten Variablen, der 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, 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

C-Dokumentation für Initialisierung