Nach der angenommen (und einzigen) Antwort für diese Frage Stack - Überlaufes ,
Konstruktor definieren mit
MyTest() = default;
initialisiert stattdessen das Objekt auf Null.
Warum macht dann das Folgende:
#include <iostream>
struct foo {
foo() = default;
int a;
};
struct bar {
bar();
int b;
};
bar::bar() = default;
int main() {
foo a{};
bar b{};
std::cout << a.a << ' ' << b.b;
}
Produziere diese Ausgabe:
0 32766
Beide definierten Konstruktoren sind Standard? Richtig? Bei POD-Typen ist die Standardinitialisierung die Nullinitialisierung.
Und gemäß der akzeptierten Antwort auf diese Frage ,
Wenn ein POD-Mitglied weder im Konstruktor noch über die C ++ 11-Initialisierung in der Klasse initialisiert wird, wird es standardmäßig initialisiert.
Die Antwort ist unabhängig von Stapel oder Haufen dieselbe.
In C ++ 98 (und nicht danach) wurde new int () als Nullinitialisierung angegeben.
Obwohl ich versucht habe, meinen (wenn auch winzigen ) Kopf um Standardkonstruktoren und Standardinitialisierung zu wickeln , konnte ich keine Erklärung finden.
quelle
bar
Der Konstruktor wird vom Benutzer bereitgestellt, währendfoo
der Konstruktor der Standard ist.a
ist Null.b
ist nicht. Scheinta
initialisiert zu sein.bar::bar()
sichtbar ist inmain()
- es kann in einer separaten Kompilierungseinheit definiert sein und etwas sehr nicht Triviales tun, währendmain()
nur in der Deklaration sichtbar ist. Ich denke, Sie werden zustimmen, dass sich dieses Verhalten nicht ändern sollte, je nachdem, ob Siebar::bar()
die Definition in einer separaten Kompilierungseinheit platzieren oder nicht (auch wenn die gesamte Situation nicht intuitiv ist).int a = 0;
willst du wirklich explizit sein ?Antworten:
Das Problem hier ist ziemlich subtil. Das würdest du denken
würde Ihnen einen vom Compiler generierten Standardkonstruktor geben, und das tut er, aber er wird jetzt als vom Benutzer bereitgestellt betrachtet. [dcl.fct.def.default] / 5 besagt:
Betonung meiner
Wir können also sehen, dass
bar()
es jetzt als vom Benutzer bereitgestellt gilt , da Sie es bei der ersten Deklaration nicht als Standard festgelegt haben . Aus diesem Grund [dcl.init] /8.2gilt nicht mehr und wir sind keine Wertinitialisierung,
b
sondern eine Standardinitialisierung gemäß [dcl.init] /8.1quelle
(*_*)
... Wenn ich überhaupt die Grundkonstrukte der Sprache verwenden will, muss ich das Kleingedruckte des Sprachentwurfs lesen, dann Halleluja! Aber es scheint wahrscheinlich das zu sein, was Sie sagen.bar::bar() = default
ist das Gleiche wiebar::bar(){}
Inline.Der Unterschied im Verhalten resultiert aus der Tatsache , dass nach
[dcl.fct.def.default]/5
,bar::bar
ist vom Benutzer bereitgestellte , wofoo::foo
keine 1 . Infolgedessenfoo::foo
werden die Mitglieder wertinitialisiert (dh nullinitialisiertfoo::a
),bar::bar
bleiben jedoch nicht initialisiert 2 .1)
[dcl.fct.def.default]/5
2)
Aus der Antwort von Vittorio Romeo
quelle
Aus der Referenz :
Bei dieser Definition
foo
handelt es sich um ein Aggregat, während diesbar
nicht der Fall ist (es verfügt über einen vom Benutzer bereitgestellten, nicht standardmäßigen Konstruktor).Daher wird für
foo
,T object {arg1, arg2, ...};
wird für die aggregierte Initialisierung Syntax.Daher
a.a
wird der Wert initialisiert, wasint
bedeutet, dass keine Initialisierung erfolgt.Für
bar
,T object {};
auf der anderen Seite ist Wert Initialisierung (der Klasseninstanz, nicht Wert Initialisierung der Mitglieder!). Da es sich um einen Klassentyp mit einem Standardkonstruktor handelt, wird der Standardkonstruktor aufgerufen. Der von Ihnen definierte Standardkonstruktor initialisiert die Mitglieder (da keine Elementinitialisierer vorhanden sind), die im Fall vonint
(mit nicht statischem Speicher)b.b
einen unbestimmten Wert hinterlassen.Nein, das ist falsch.
PS Ein Wort zu Ihrem Experiment und Ihrer Schlussfolgerung: Wenn Sie sehen, dass die Ausgabe Null ist, bedeutet dies nicht unbedingt, dass die Variable mit Null initialisiert wurde. Null ist eine durchaus mögliche Zahl für einen Müllwert.
Die Tatsache, dass der Wert mehrmals gleich war, bedeutet nicht unbedingt, dass er auch initialisiert wurde.
Die Tatsache, dass das Ergebnis bei mehreren Compileroptionen gleich ist, bedeutet nicht, dass die Variable initialisiert wird. (Obwohl in einigen Fällen das Ändern der Standardversion ändern kann, ob sie initialisiert wird).
In C ++ gibt es keine garantierte Möglichkeit, einen nicht initialisierten Wert ungleich Null anzuzeigen.
Die einzige Möglichkeit zu wissen, dass eine Variable initialisiert wird, besteht darin, das Programm mit den Regeln der Sprache zu vergleichen und zu überprüfen, ob die Regeln besagen, dass es initialisiert ist. In diesem Fall
a.a
wird tatsächlich initialisiert.quelle
a
wird initialisiert. Ich dachte,a
die Standardinitialisierung und die Standardinitialisierung für einen Mitglieds-POD ist die Nullinitialisierung. Kommta
dann zum Glück immer auf Null, egal wie oft ich dieses Programm starte.Then how come a is initialized.
Da es sich um einen initialisierten Wert handelt.I was thinking a is default initialized
Es ist nicht.Meh, ich habe versucht, das von Ihnen bereitgestellte Snippet
test.cpp
über gcc & clang und mehrere Optimierungsstufen auszuführen:Hier wird es also interessant. Es zeigt deutlich, dass der Clang-O0-Build Zufallszahlen liest, vermutlich Stapelspeicher.
Ich drehte schnell meine IDA auf, um zu sehen, was passiert:
Was
bar::bar(bar *this)
macht nun?Hmm, nichts. Wir mussten auf die Montage zurückgreifen:
Also ja, es ist einfach nichts, was der Konstruktor im Grunde tut
this = this
. Wir wissen jedoch, dass tatsächlich zufällige nicht initialisierte Stapeladressen geladen und gedruckt werden.Was ist, wenn wir explizit Werte für die beiden Strukturen angeben?
Hit up clang, oopsie:
Ähnliches Schicksal auch bei g ++:
Dies bedeutet also, dass es sich effektiv um eine direkte Initialisierung handelt
bar b(0)
, nicht um eine aggregierte Initialisierung.Dies liegt wahrscheinlich daran, dass dies möglicherweise ein externes Symbol sein kann, wenn Sie keine explizite Konstruktorimplementierung bereitstellen, z.
Der Compiler ist nicht klug genug, um dies in einer nicht optimierten Phase als No-Op / Inline-Aufruf abzuleiten.
quelle