Die Notwendigkeit der Initialisierung ohne Konstruktor
(consteval.ca)Ich habe keinen Konstruktor und muss initialisiert werden
-
Einleitung
- Als ich anfing, C++ zu lernen, habe ich gelernt, in welchen Fällen der Compiler einen Standardkonstruktor bereitstellt.
- Dabei kam die Frage auf, welches Risiko besteht, dass ein Objekt in bestimmten Situationen nicht initialisiert wird.
-
Standardinitialisierung und Wertinitialisierung
T t;führt eine Standardinitialisierung aus.- Wenn
Tein Klassentyp ist und einen Standardkonstruktor hat, wird dieser ausgeführt. - Wenn
Tein Array-Typ ist, wird jedes Element standardinitialisiert. - Andernfalls passiert nichts.
- Wenn
T t{};führt eine Wertinitialisierung aus.- Wenn
Tein Klassentyp ist und keinen Standardkonstruktor hat oder einen benutzerdefinierten bzw. gelöschten Standardkonstruktor besitzt, wird standardinitialisiert. - Andernfalls wird zuerst mit 0 initialisiert und danach standardinitialisiert.
- Wenn
Tein Array-Typ ist, wird jedes Element wertinitialisiert. - Andernfalls wird mit 0 initialisiert.
- Wenn
-
Standardkonstruktor
- Wenn kein Standardkonstruktor deklariert wird, deklariert der Compiler implizit einen Standardkonstruktor.
- Ein implizit deklarierter Standardkonstruktor hat einen leeren Funktionsrumpf und eine leere Member-Initialisierungsliste.
- Beispiel:
struct T { int x; T() = default; }; T t{}; std::cout << t.x << std::endl; // Ausgabe ist 0
-
Implizit definierter Standardkonstruktor
- Wenn ein Standardkonstruktor implizit deklariert oder explizit als Standard deklariert wird, stellt der Compiler einen implizit definierten Standardkonstruktor bereit.
- Beispiel:
struct T { T(); }; T::T() = default; T t{}; std::cout << t.x << std::endl; // Ausgabe ist ein Garbage-Wert
-
Fälle, in denen kein Standardkonstruktor bereitgestellt werden kann
- Wenn
Tein nichtstatisches Referenz-Member besitzt - Wenn
Tein nichtstatisches Member oder eine nichtabstrakte Basisklasse besitzt, die weder standardkonstruiert noch zerstört werden kann - Wenn
Teinconst-nichtstatisches Member ohne Default-Member-Initializer besitzt
- Wenn
-
Korrekte Initialisierung
T t{};führt eine Listeninitialisierung aus.- Listeninitialisierung wird in direkte Listeninitialisierung und Copy-Listeninitialisierung unterteilt.
- Beispiel:
struct S { int a; float b; char c; }; S s{3, 4.0f, 'S'}; // kein Konstruktoraufruf
-
Listeninitialisierung und Aggregatinitialisierung
- Aggregatinitialisierung ist eine spezielle Form der Listeninitialisierung, bei der jedes Element einer Klasse oder eines Arrays per Copy-Initialisierung mit dem entsprechenden Element der Initialisierungsliste initialisiert wird.
- Beispiel:
struct A { const int x; }; A a{}; // a.x wird mit 0 initialisiert
-
Initialisierung mit Klammern
- Initialisierung mit Klammern führt eine direkte Nicht-Listeninitialisierung aus.
- Beispiel:
struct T { const int& r; }; T t(42); // t.r ist eine Referenz auf 42
-
Zusammenfassung
- Die Initialisierungsregeln sind komplex, aber durch das direkte Schreiben eines Konstruktors lassen sich die meisten Probleme vermeiden.
- Statt sich auf den Compiler zu verlassen, ist es besser, den Konstruktor selbst zu schreiben.
Meinung von GN⁺
- Dieser Artikel erklärt die Komplexität der C++-Initialisierungsregeln sehr gut.
- Das Verständnis der Initialisierungsregeln in C++ ist wichtig, da es große Auswirkungen auf Stabilität und Performance des Codes hat.
- Einen Konstruktor selbst zu schreiben, ist der beste Weg, Initialisierungsprobleme zu vermeiden.
- Eine andere Sprache mit ähnlicher Funktionalität ist Rust; dort sind die Initialisierungsregeln klarer.
- Bei der Einführung neuer Technologien ist es wichtig, auch Details wie Initialisierungsregeln genau zu verstehen und korrekt anzuwenden.
1 Kommentare
Hacker-News-Kommentar
Das Ergebnis der Initialisierung von
twird 0 seintwertinitialisiert wird undTkeinen benutzerdefinierten Standardkonstruktor hat; daher wird das Objekt erst nullinitialisiert und danach der Standardkonstruktor aufgerufenDer Standardkonstruktor standardinitialisiert die Member, was sich von der Wertinitialisierung unterscheidet
GCC scheint dem zuzustimmen
Der Autor hat übersehen, dass er tatsächlich
xwertinitialisiertDie Details der Regeln sind komplex und teils unvernünftig
Es wäre eine große Verbesserung, die Standardinitialisierung explizit ausdrücken zu können
std::array<int, 100> = void;wäre besserDas Bindeglied zwischen Listeninitialisierung und Aggregatinitialisierung ist, dass bei einer Listeninitialisierung eines Aggregats eine Aggregatinitialisierung durchgeführt wird
Der Fall mit einem Element verhält sich anders als der mit zwei oder mehr Elementen
Man kann einen eigenen Konstruktor schreiben und damit ein Tupel oder Array mit nur einem Element initialisieren
Als die C++11-Initialisierungsliste neu war, habe ich das entdeckt und dachte, das sei völlig verrückt
Erwähnung von "I Have No Mouth, and I Must Scream" (1967)
Verwendung der Syntax
T::T() = default;Man erwartet, dass die Ausgabe 0 ist, tatsächlich wird aber ein Garbage-Wert ausgegeben
Dadurch können Nutzer einer Bibliothek das Verhalten der Bibliothek ändern
Wer noch mehr C++-Komplexität will, dem sei die C++ FQA empfohlen
Das Blog-Theme ist von Computern aus der DEC-Ära inspiriert, aber sauber und minimalistisch
Beim Lesen davon wird einem schwindelig
Go und Rust haben keine speziellen Konstruktoren, was vieles vereinfacht
Ich frage mich, ob es ein C++-Tool gibt, das alle impliziten Verhaltensweisen sichtbar macht
Es werden falsche Informationen über die Klasse gegeben
Die Behauptung,
T t;tue "gar nichts", ist falschT t;fehlIm Blog-Header ist ein DEC-Frontpanel zu sehen