Was bedeuten die folgenden Sätze in C ++: Null-, Standard- und Wertinitialisierung?

188

Was bedeuten die folgenden Sätze in C ++:

  • Nullinitialisierung,

  • Standardinitialisierung und

  • Wertinitialisierung

Was sollte ein C ++ - Entwickler über sie wissen?

Rechnung
quelle
1
Dies ist im Zusammenhang mit (aber nicht identisch mit) stackoverflow.com/questions/620137/…
Steve Jessop
20
Es gibt mehr! Die vollständige Liste der Initialisierungen: Wert, Direkt, Kopieren, Liste (neues C ++ 11-Intro), Aggregat, Referenz, Null, Konstante und Standard; en.cppreference.com/w/cpp/language/initialization listet alle mit Beispielen auf :)
legends2k

Antworten:

64

Zu erkennen ist, dass die 'Wertinitialisierung' mit dem C ++ 2003-Standard neu ist - sie existiert nicht im ursprünglichen Standard von 1998 (ich denke, dies ist möglicherweise der einzige Unterschied, der mehr als eine Klarstellung ist). Siehe die Antwort von Kirill V. Lyadvinsky für die Definitionen direkt aus dem Standard.

In dieser vorherigen Antwort zum Verhalten von finden Sie operator newDetails zum unterschiedlichen Verhalten dieser Art der Initialisierung und wann sie aktiviert werden (und wann sie sich von c ++ 98 bis C ++ 03 unterscheiden):

Der Hauptpunkt der Antwort ist:

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

  • In C ++ 1998 gibt es zwei Arten der Initialisierung: Null und Standard
  • In C ++ 2003, einem dritten Initialisierungstyp, wurde die Wertinitialisierung hinzugefügt.

Um es gelinde auszudrücken, es ist ziemlich komplex und wenn die verschiedenen Methoden zum Einsatz kommen, sind sie subtil.

Zu beachten ist, dass MSVC auch in VS 2008 (VC 9 oder cl.exe Version 15.x) den C ++ 98-Regeln folgt.

Das folgende Snippet zeigt, dass MSVC und Digital Mars den C ++ 98-Regeln folgen, während GCC 3.4.5 und Comeau den C ++ 03-Regeln folgen:

#include <cstdio>
#include <cstring>
#include <new>

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

int main()
{
    char buf[sizeof(B)];
    std::memset( buf, 0x5a, sizeof( buf));

    // use placement new on the memset'ed buffer to make sure 
    //  if we see a zero result it's due to an explicit 
    //  value initialization
    B* pB = new(buf) B();   //C++98 rules - pB->m is uninitialized
                            //C++03 rules - pB->m is set to 0
    std::printf( "m  is %d\n", pB->m);
    return 0;
}
Michael Burr
quelle
1
Nicht, dass es wichtig wäre int, aber m()in der dritten Zeile initialisiert der Wert m. Wichtig , wenn Sie ändern int m;zu B m;. :)
Johannes Schaub - litb
Richtig - Aund Cwerden in diesem Beispiel nicht verwendet (sie werden von der anderen verknüpften Antwort übernommen). Obwohl C ++ 98 und C ++ 03 unterschiedliche Begriffe verwenden, um zu beschreiben, wie Aund wie sie aufgebaut Csind, ist das Ergebnis in beiden Standards das gleiche. Führt nur struct Bzu unterschiedlichem Verhalten.
Michael Burr
1
Was ich damit gemeint habe ist, dass wenn Sie C in ändern struct C { C() : m() {}; ~C(); B m; };, dann haben Sie m.m0. Aber wenn es standardmäßig initialisiert würde, mwie Sie sagen, dass C ++ 03 es tut, dann m.mwürde es nicht wie in C ++ 98 initialisiert werden.
Johannes Schaub - litb
1
Weitere interessante Kommentare zur MSVC-Behandlung dieser Funktion: stackoverflow.com/questions/3931312/…
nobar
Welche Initialisierung findet statt, wenn Sie Ihren Typ als lokale Variable deklarieren, dh am Stapel?
André Puel
87

C ++ 03 Standard 8.5 / 5:

Ein Objekt vom Typ T auf Null zu initialisieren bedeutet:
- Wenn T ein Skalartyp (3.9) ist, wird das Objekt auf den Wert 0 (Null) gesetzt, der in T umgewandelt wird;
- Wenn T ein Nicht-Vereinigungsklassentyp ist, wird jedes nicht statische Datenelement und jedes Basisklassen-Unterobjekt mit Null initialisiert.
- Wenn T ein Vereinigungstyp ist, wird das zuerst benannte Datenelement des Objekts mit Null initialisiert.
- Wenn T ein Array-Typ ist, wird jedes Element mit Null initialisiert.
- Wenn T ein Referenztyp ist, wird keine Initialisierung durchgeführt.

Die Standardinitialisierung eines Objekts vom Typ T bedeutet:
- Wenn T ein Nicht-POD-Klassentyp ist (Abschnitt 9), wird der Standardkonstruktor für T aufgerufen (und die Initialisierung ist fehlerhaft, wenn T keinen zugänglichen Standardkonstruktor hat);
- Wenn T ein Array-Typ ist, wird jedes Element standardmäßig initialisiert.
- Andernfalls wird das Objekt mit Null initialisiert.

Die Wertinitialisierung eines Objekts vom Typ T bedeutet:
- Wenn T ein Klassentyp (Klausel 9) mit einem vom Benutzer deklarierten Konstruktor (12.1) ist, wird der Standardkonstruktor für T aufgerufen (und die Initialisierung ist falsch, wenn T. hat keinen zugänglichen Standardkonstruktor);
- Wenn T ein nicht vereinigter Klassentyp ohne einen vom Benutzer deklarierten Konstruktor ist, wird jedes nicht statische Datenelement und jede Basisklassenkomponente von T wertinitialisiert.
- Wenn T ein Array-Typ ist, wird jedes Element mit einem Wert initialisiert.
- Andernfalls wird das Objekt mit Null initialisiert

Ein Programm, das die Standardinitialisierung oder Wertinitialisierung einer Entität des Referenztyps fordert, ist fehlerhaft. Wenn T ein cv-qualifizierter Typ ist, wird die cv-nicht qualifizierte Version von T für diese Definitionen von Nullinitialisierung, Standardinitialisierung und Wertinitialisierung verwendet.

Kirill V. Lyadvinsky
quelle
17
Dies ist möglicherweise für C ++ 11 veraltet. cppreference.com gibt an, dass die Standardinitialisierung keine Mitglieder auf Null initialisiert (nur die Wertinitialisierung ).
Alexei Sholik
3
@android wirft einen wichtigen Punkt auf, den ich anderswo nicht beantwortet sehe, deshalb habe ich eine neue Frage gestellt. stackoverflow.com/questions/22233148/…
Adrian McCarthy