wake-up-neo.com

Standard-, Wert- und Null-Initialisierungsfehler

Ich bin sehr verwirrt über Wert- & Standard- & Null-Initialisierung. und besonders, wenn sie sich für die verschiedenen Standards einsetzen C++ 03 und C++ 11 (und C++ 14).

Ich zitiere und versuche eine wirklich gute Antwort zu erweitern Value-/Default-/Zero-Init C++ 98 und C++ 03 = hier, um es allgemeiner zu machen, da es vielen Benutzern helfen würde, wenn jemand helfen könnte, die erforderlichen Lücken auszufüllen, um einen guten Überblick darüber zu haben, was wann passiert?

Die vollständige Einsicht anhand von Beispielen auf den Punkt gebracht:

Manchmal wird der vom Operator new zurückgegebene Speicher initialisiert, und manchmal hängt er nicht davon ab, ob der Typ, den Sie neu erstellen, ein POD (plain old data) ist, oder ob es sich um eine Klasse handelt, die enthält POD-Mitglieder und verwendet einen vom Compiler generierten Standardkonstruktor.

  • In C++ 1998 gibt es zwei Arten der Initialisierung: Null - und Standardinitialisierung
  • In C++ 2003 einer dritten Art der Initialisierung wurde value-initialization hinzugefügt.
  • In C++ 2011/C++ 2014 wurde nur Listeninitialisierung hinzugefügt und die Regeln für Wert-/Standard-/Null- Initialisierung etwas geändert.

Annehmen:

struct A { int m; };                     
struct B { ~B(); int m; };               
struct C { C() : m(){}; ~C(); int m; };  
struct D { D(){}; int m; };             
struct E { E() = default; int m;} /** only possible in c++11/14 */  
struct F {F(); int m;}  F::F() = default; /** only possible in c++11/14 */

In einem C++ 98-Compiler sollte Folgendes auftreten :

  • new A - unbestimmter Wert (A ist POD)
  • new A()-zero-initialize
  • new B - Standardkonstrukt (B::m Ist nicht initialisiert, B ist nicht POD)
  • new B() - Standardkonstrukt (B::m ist nicht initialisiert)
  • new C - Standardkonstrukt (C::m Ist null-initialisiert, C ist kein POD)
  • new C() - Standardkonstrukt (C::m ist null-initialisiert)
  • new D - Standardkonstrukt (D::m Ist nicht initialisiert, D ist nicht POD)
  • new D() - default construct? (D::m ist nicht initialisiert)

In einem C++ 03-konformen Compiler sollten die Dinge so funktionieren:

  • new A - unbestimmter Wert (A ist POD)
  • new A() - value-initialize A, eine Nullinitialisierung, da es sich um einen POD handelt.
  • new B - Standardinitialisierung (lässt B::m Uninitialisiert, B ist kein POD)
  • new B() - value-initializes B, wodurch alle Felder auf Null gesetzt werden, da der Standard-Ctor nicht benutzerdefiniert, sondern vom Compiler generiert wird.
  • new C - default-initialisiert C, wodurch der Standard-Ctor aufgerufen wird. (C::m Ist null-initialisiert, C ist kein POD)
  • new C() - value-initializes C, das den Standard-ctor aufruft. (C::m Ist null-initialisiert)
  • new D - Standardkonstrukt (D::m Ist nicht initialisiert, D ist nicht POD)
  • new D() - value-initializes D?, das den Standard-ctor aufruft (D::m ist nicht initialisiert)

Kursiv und? sind unsicherheiten, bitte helfen sie dies zu korrigieren :-)

In einem C++ 11-konformen Compiler sollten die Dinge so funktionieren:

??? (bitte hilf wenn ich hier anfange wird es trotzdem schief gehen)

In einem C++ 14-konformen Compiler sollten die Dinge so funktionieren: ??? (Bitte helfen Sie, wenn ich hier anfange, wird es trotzdem schief gehen) (Entwurf basiert auf Antwort)

  • new A - initialisiert standardmäßig A, Compilergen. ctor, (Leavs A::m nicht initialisiert) (A ist POD)
  • new A() - value-initializes A, das ist die Nullinitialisierung seit dem 2. Punkt in [dcl.init]/8

  • new B - initialisiert standardmäßig B, Compilergen. ctor, (Leavs B::m nicht initialisiert) (B ist kein POD)

  • new B() - value-initializes B, wodurch alle Felder auf Null gesetzt werden, da der Standard-Ctor nicht benutzerdefiniert, sondern vom Compiler generiert wird.
  • new C - default-initialisiert C, wodurch der Standard-Ctor aufgerufen wird. (C::m Ist null-initialisiert, C ist kein POD)
  • new C() - value-initializes C, das den Standard-ctor aufruft. (C::m Ist null-initialisiert)
  • new D - Standardinitialisiert D (D::m Ist nicht initialisiert, D ist nicht POD)
  • new D() - value-initializes D, das den Standard-ctor aufruft (D::m ist nicht initialisiert)
  • new E - initialisiert standardmäßig E, wodurch die comp aufgerufen wird. gen. ctor. (E::m Ist nicht initialisiert, E ist nicht POD)
  • new E() - value-initializes E, wobei E seit 2 Punkten in [dcl.init]/8 auf Null gesetzt wird)
  • new F - initialisiert standardmäßig F, wodurch die comp aufgerufen wird. gen. ctor. (F::m Ist nicht initialisiert, F ist nicht POD)
  • new F() - value-initializes F, wobei default-initializes F seit 1. point in [dcl.init]/8 (F ctor-Funktion wird vom Benutzer bereitgestellt, wenn sie vom Benutzer deklariert und bei der ersten Deklaration nicht explizit als Standard festgelegt oder gelöscht wird. Link )
80
Gabriel

C++ 14 gibt die Initialisierung von Objekten an, die mit new in [Ausdruckneu]/17 ([Ausdruckneu]/15 in C++ 11) erstellt wurden, und die Notiz war keine Notiz, sondern normativer Text dann):

Ein new-expression, der ein Objekt vom Typ T erstellt, initialisiert dieses Objekt wie folgt:

  • Wenn new-initializer weggelassen wird, lautet das Objekt default-initialized (8.5). [ Hinweis: Wenn keine Initialisierung durchgeführt wird, hat das Objekt einen unbestimmten Wert. — Endnote]
  • Andernfalls wird der Neuinitialisierer gemäß den Initialisierungsregeln von 8.5 für Direktinitialisierung interpretiert.

Die Standardinitialisierung ist in [dcl.init]/7 definiert (/ 6 in C++ 11 und der Wortlaut selbst hat den gleichen Effekt):

default-initialize Ein Objekt vom Typ T bedeutet:

  • wenn T ein (möglicherweise von cv qualifizierter) Klassentyp ist (Klausel 9), wird der Standardkonstruktor (12.1) für T aufgerufen (und die Initialisierung ist falsch, wenn T hat keinen Standardkonstruktor oder Überladungsauflösung (13.3) führt zu einer Mehrdeutigkeit oder zu einer Funktion, die aus dem Kontext der Initialisierung gelöscht wird oder auf die nicht zugegriffen werden kann.
  • wenn T ein Array-Typ ist, ist jedes Element default-initialisiert;
  • andernfalls wird keine Initialisierung durchgeführt.

Somit

  • new A Ruft nur den Standardkonstruktor von A auf, der m nicht initialisiert. Unbestimmter Wert. Sollte für new B Gleich sein.
  • new A() wird gemäß [dcl.init]/11 (/ 10 in C++ 11) interpretiert:

    Ein Objekt, dessen Initialisierung ein leerer Satz von Klammern ist, d. H. (), Soll mit einem Wert initialisiert werden.

    Und nun betrachten wir [dcl.init]/8 (/ 7 in C++ 11 †):

    value-initialize Ein Objekt vom Typ T bedeutet:

    • wenn T ein (möglicherweise von cv qualifizierter) Klassentyp (Klausel 9) ohne Standardkonstruktor (12.1) oder mit einem vom Benutzer bereitgestellten oder gelöschten Standardkonstruktor ist, wird das Objekt standardmäßig initialisiert.
    • Wenn T ein (möglicherweise von cv qualifizierter) Klassentyp ohne einen vom Benutzer angegebenen oder gelöschten Standardkonstruktor ist, wird das Objekt mit Null initialisiert und die semantischen Einschränkungen für die Standardinitialisierung werden überprüft Wenn T einen nicht trivialen Standardkonstruktor hat, wird das Objekt standardmäßig initialisiert;
    • wenn T ein Array-Typ ist, wird jedes Element mit einem Wert initialisiert.
    • andernfalls wird das Objekt mit Null initialisiert.

    Daher wird new A()m auf Null setzen. Und dies sollte für A und B äquivalent sein.

  • new C Und new C() initialisieren das Objekt erneut standardmäßig, da der erste Aufzählungspunkt aus dem letzten Anführungszeichen gilt (C verfügt über einen vom Benutzer angegebenen Standardkonstruktor!). In beiden Fällen wird jetzt jedoch m im Konstruktor initialisiert.


† Nun, dieser Absatz hat in C++ 11 einen etwas anderen Wortlaut, der das Ergebnis nicht ändert:

value-initialize Ein Objekt vom Typ T bedeutet:

  • wenn T ein (möglicherweise von cv qualifizierter) Klassentyp (Klausel 9) mit einem vom Benutzer bereitgestellten Konstruktor (12.1) ist, wird der Standardkonstruktor für T aufgerufen (und die Initialisierung ist nicht korrekt) -geformt, wenn T keinen zugänglichen Standardkonstruktor hat);
  • wenn T ein (möglicherweise von cv qualifizierter) Nicht-Union-Klassentyp ohne einen vom Benutzer angegebenen Konstruktor ist, wird das Objekt mit Null initialisiert, und wenn T implizit als Standardkonstruktor deklariert ist, ist dies kein Konstruktor -trivial, dieser Konstruktor wird aufgerufen.
  • wenn T ein Array-Typ ist, wird jedes Element mit einem Wert initialisiert.
  • andernfalls wird das Objekt mit Null initialisiert.
24
Columbo

Die folgende Antwort erweitert die Antwort https://stackoverflow.com/a/620402/977038 , die als Referenz für C++ 98 und C++ 03 dienen würde

Zitiere die Antwort

  1. In C++ 1998 gibt es zwei Arten der Initialisierung: Null und Standard
  2. In C++ 2003 wurde eine dritte Art der Initialisierung, die Wertinitialisierung, hinzugefügt.

C++ 11 (In Bezug auf n3242)

Initialisierer

8.5 Initialisierer [dcl.init] gibt an, dass ein variabler POD oder ein Nicht-POD entweder als Klammer-oder-Gleich-Initialisierer initialisiert werden kann, der entweder Klammer-Init-Liste sein kann oder Initialisierungsklausel zusammenfassend bezeichnet als Klammer-oder-Gleichheitsinitialisierung oder mit (Ausdrucksliste). Vor C++ 11 wurde nur (Ausdrucksliste) oder Initialisierungsklausel unterstützt, obwohl Initialisierungsklausel war eingeschränkter als das, was wir in C++ 11 haben. In C++ 11 unterstützt Initialisierungsklausel jetzt geschweifte Init-Liste abgesehen von Zuweisungsausdruck wie gehabt in C++ 03. Die folgende Grammatik fasst die neue unterstützte Klausel zusammen, in der der fett gedruckte Teil im C++ 11-Standard neu hinzugefügt wurde.

initialisierer:
klammer-oder-gleich-Initialisierer
(Ausdrucksliste)
klammer-oder-gleich-Initialisierer:
= Initialisierungsklausel
klammer-Init-Liste
Initialisierungsklausel:
& NbZuweisungsausdruck
klammer-Init-Liste
initialisierer-Liste:
initialisierungsklausel ... opt
initializer-liste, initializer-klausel ... opt **
Klammer-Init-Liste:
{Initialisierungsliste, opt}
{}

Initialisierung

Wie C++ 03 unterstützt C++ 11 weiterhin drei Arten der Initialisierung


Hinweis

Der fett hervorgehobene Teil wurde in C++ 11 hinzugefügt, und der durchgestrichene Teil wurde aus C++ 11 entfernt.

  1. Initialisierertyp: 8.5.5 [dcl.init] _zero-initialize_

Wird in folgenden Fällen ausgeführt

  • Objekte mit statischer oder Thread-Speicherdauer werden mit Null initialisiert
  • Wenn weniger Initialisierer als Array-Elemente vorhanden sind, muss jedes nicht explizit initialisierte Element mit Null initialisiert werden
  • Wenn T während value-initialize ein (möglicherweise cv-qualifizierter) Nicht-Union-Klassentyp ohne einen vom Benutzer angegebenen Konstruktor ist, wird das Objekt mit Null initialisiert.

Ein Objekt oder eine Referenz vom Typ T auf Null zu initialisieren bedeutet:

  • wenn T ein Skalartyp (3.9) ist, wird das Objekt auf den Wert 0 (Null) gesetzt als integraler konstanter Ausdruck genommen, umgerechnet in T;
  • wenn T ein (möglicherweise lebenslaufqualifiziert) nicht gewerkschaftlicher Klassentyp ist, jedes nicht statische Datenelement und Jedes Basisklassen-Unterobjekt wird mit Null initialisiert und das Auffüllen wird mit Null-Bits initialisiert;
  • wenn T ein (möglicherweise lebenslaufqualifiziert) Union-Typ ist, ist das erste nicht statisch benannte Datenelement des Objekts Null initialisiert und Auffüllen wird auf Null Bits initialisiert;
  • wenn T ein Array-Typ ist, wird jedes Element mit Null initialisiert.
  • wenn T ein Referenztyp ist, wird keine Initialisierung durchgeführt.

2. Initialisierertyp: 8.5.6 [dcl.init] _default-initialize_

Wird in den folgenden Fällen ausgeführt

  • Wenn der Neuinitialisierer weggelassen wird, wird das Objekt standardmäßig initialisiert. Wird keine Initialisierung durchgeführt, hat das Objekt einen unbestimmten Wert.
  • Wenn für ein Objekt kein Initialisierer angegeben ist, wird das Objekt standardmäßig initialisiert, mit Ausnahme von Objekten mit statischer oder Thread-Speicherdauer
  • Wenn eine Basisklasse oder ein nicht statisches Datenelement in einer Konstruktorinitialisierungsliste nicht erwähnt wird und dieser Konstruktor aufgerufen wird.

Das Standardinitialisieren eines Objekts vom Typ T bedeutet:

  • wenn T a ist (möglicherweise lebenslaufqualifiziert) nicht-POD Klassentyp (Klausel 9), der Standardkonstruktor für T wird aufgerufen (und die Initialisierung ist falsch, wenn T keinen zugänglichen Standardkonstruktor hat);
  • wenn T ein Array-Typ ist, wird jedes Element standardmäßig initialisiert.
  • andernfalls wird keine Initialisierung durchgeführt.

Hinweis Bis C++ 11 wurden nur Nicht-POD-Klassentypen mit automatischer Speicherdauer als standardmäßig initialisiert betrachtet, wenn kein Initialisierer verwendet wurde.


3. Initialisierertyp: 8.5.7 [dcl.init] _value-initialize_

  1. Wenn ein Objekt (namenlos temporär, benannte Variable, dynamische Speicherdauer oder nicht statisches Datenelement), dessen Initialisierung ein leerer Satz von Klammern ist, d. H. () Oder geschweifte Klammern {}

Ein Objekt vom Typ T mit einem Wert zu initialisieren bedeutet:

  • wenn T ein (möglicherweise lebenslaufqualifiziert) Klassentyp (Klausel 9) mit einem vom Benutzer bereitgestellten Konstruktor ( 12.1), dann wird der Standardkonstruktor für T aufgerufen (und die Initialisierung ist fehlerhaft, wenn T keinen zugänglichen Standardkonstruktor hat);
  • wenn T ein (möglicherweise von cv qualifizierter) Nicht-Union-Klassentyp ohne einen vom Benutzer angegebenen Konstruktor ist, dann wird jedes nicht statische Datenelement und jede Basisklassenkomponente von T mit einem Wert initialisiert;  dann wird das Objekt mit Null initialisiert, und wenn der implizit deklarierte Standardkonstruktor von T nicht trivial ist, wird dieser Konstruktor aufgerufen.
  • wenn T ein Array-Typ ist, wird jedes Element mit einem Wert initialisiert.
  • andernfalls wird das Objekt mit Null initialisiert.

Also, um es zusammenzufassen

Hinweis Das relevante Zitat aus der Norm ist in Fettdruck hervorgehoben.

  • new A: default-initializes (lässt A :: m nicht initialisiert)
  • new A(): Null-Initialisierung von A, da der mit dem Wert initialisierte Kandidat keinen vom Benutzer bereitgestellten oder gelöschten Standardkonstruktor hat. if T ist ein (möglicherweise von cv qualifizierter) Nicht-Union-Klassentyp ohne einen vom Benutzer bereitgestellten Konstruktor. Dann wird das Objekt mit Null initialisiert, und wenn der implizit deklarierte Standardkonstruktor von T nicht trivial ist, wird dieser Konstruktor aufgerufen.
  • new B: default-initializes (lässt B :: m nicht initialisiert)
  • new B(): value-initializes B, das alle Felder mit Null initialisiert; wenn T ein (möglicherweise lebenslaufqualifizierter) Klassentyp ist (Klausel 9) mit einem vom Benutzer bereitgestellten Konstruktor (12.1), dann heißt der Standardkonstruktor für T
  • new C: default-initialisiert C, das den Standard-Ctor aufruft. Wenn T ein (möglicherweise lebenslaufqualifizierter) Klassentyp ist (Klausel 9), wird der Standardkonstruktor für T aufgerufen Wird der Neuinitialisierer weggelassen, wird das Objekt standardmäßig initialisiert
  • new C(): value-initializes C, das den Standard-ctor aufruft. wenn T ein (möglicherweise cv-qualifizierter) Klassentyp ist (Klausel 9) mit einem vom Benutzer bereitgestellten Konstruktor (12.1), dann wird der Standardkonstruktor für T aufgerufen. Außerdem Ein Objekt, dessen Initialisierung eine leere Klammer ist, dh (), soll mit einem Wert initialisiert werden
12
Abhijit