De acuerdo con la respuesta aceptada (y única) para esta pregunta de desbordamiento de pila ,
Definiendo el constructor con
MyTest() = default;
en su lugar, inicializará a cero el objeto.
Entonces, ¿por qué lo siguiente,
#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;
}
producir esta salida:
0 32766
Ambos constructores definidos son predeterminados? ¿Correcto? Y para los tipos de POD, la inicialización predeterminada es la inicialización cero.
Y de acuerdo con la respuesta aceptada para esta pregunta ,
Si un miembro POD no se inicializa en el constructor ni a través de la inicialización en clase de C ++ 11, se inicializa por defecto.
La respuesta es la misma independientemente de la pila o el montón.
En C ++ 98 (y no después), se especificó new int () como que realizaba inicialización cero.
A pesar de tratar de comprender mi (aunque pequeña ) idea de los constructores predeterminados y la inicialización predeterminada , no pude encontrar una explicación.
fuente
bar
El constructor es proporcionado por el usuario, mientras quefoo
el constructor es el predeterminado.a
es cerob
no es. Parece quea
se inicializa.bar::bar()
sea visible enmain()
- podría definirse en una unidad de compilación separada y hacer algo muy no trivial mientrasmain()
solo esté visible la declaración. Creo que estará de acuerdo en que este comportamiento no debería cambiar dependiendo de si colocabar::bar()
la definición en una unidad de compilación separada o no (incluso si toda la situación no es intuitiva).int a = 0;
quieres ser realmente explícito?Respuestas:
El problema aquí es bastante sutil. Pensarías que
le daría un constructor predeterminado generado por el compilador, y lo hace, pero ahora se considera proporcionado por el usuario. [dcl.fct.def.default] / 5 estados:
énfasis mío
Por lo tanto, podemos ver que, dado que no se omitió
bar()
cuando lo declaró por primera vez, ahora se considera proporcionado por el usuario. Por eso [dcl.init] /8.2ya no se aplica y no estamos inicializando el valor,
b
sino que lo inicializamos por defecto según [dcl.init] /8.1fuente
(*_*)
... Si incluso usar las construcciones básicas del lenguaje, necesito leer la letra pequeña del borrador del lenguaje, entonces ¡Aleluya! Pero probablemente parece ser lo que dices.bar::bar() = default
fuera de línea es lo mismo que hacerlo enbar::bar(){}
línea.La diferencia en el comportamiento proviene del hecho de que, según
[dcl.fct.def.default]/5
,bar::bar
es proporcionado por el usuario dondefoo::foo
no es 1 . Como consecuencia de ello,foo::foo
se valorará a inicializar sus miembros (es decir: de cero a inicializarfoo::a
), perobar::bar
se quedará sin inicializar 2 .1)
[dcl.fct.def.default]/5
2)
De la respuesta de Vittorio Romeo
fuente
De cppreference :
Dada esta definición,
foo
es un agregado, mientrasbar
que no lo es (tiene un constructor no predeterminado proporcionado por el usuario).Por lo tanto
foo
, para ,T object {arg1, arg2, ...};
es la sintaxis para la inicialización agregada.Por
a.a
lo tanto, se inicializa el valor, queint
significa inicialización cero.Para
bar
,T object {};
por el contrario es el valor de inicialización (de la instancia de clase, no el valor de inicialización de miembros!). Como es un tipo de clase con un constructor predeterminado, se llama al constructor predeterminado. El constructor predeterminado que definió default inicializa los miembros (en virtud de no tener inicializadores de miembros), que en el caso deint
(con almacenamiento no estático) dejab.b
un valor indeterminado.No. Esto está mal.
PD: Una palabra sobre su experimento y su conclusión: Ver que la salida es cero no significa necesariamente que la variable haya sido inicializada en cero. Cero es un número perfectamente posible para un valor basura.
El hecho de que el valor fuera el mismo varias veces no significa necesariamente que se haya inicializado tampoco.
El hecho de que el resultado sea el mismo con múltiples opciones de compilación no significa que la variable se inicialice. (Aunque en algunos casos, cambiar la versión estándar puede cambiar si se inicializa).
No hay forma garantizada en C ++ de hacer que el valor del valor no inicializado parezca distinto de cero.
La única forma de saber que una variable se inicializa es comparar el programa con las reglas del lenguaje y verificar que las reglas dicen que se inicializó. En este caso, de
a.a
hecho, se inicializa.fuente
a
es que se inicializa? Estaba pensandoa
que la inicialización predeterminada y la inicialización predeterminada para un POD miembro es, inicialización cero. Esa
entonces sólo por suerte siempre viene cero, no importa cuántas veces he ejecutar este programa.Then how come a is initialized.
Porque es un valor inicializado.I was thinking a is default initialized
No lo es.Meh, intenté ejecutar el fragmento que proporcionaste como
test.cpp
, a través de gcc & clang y múltiples niveles de optimización:Entonces, ahí es donde se pone interesante, muestra claramente que la construcción de C0 O está leyendo números aleatorios, presumiblemente acumula espacio.
Rápidamente encendí mi IDA para ver qué está pasando:
Ahora bien, ¿qué
bar::bar(bar *this)
hace?Hmm, nada Tuvimos que recurrir al uso de ensamblaje:
Entonces sí, es simplemente nada, lo que básicamente hace el constructor
this = this
. Pero sabemos que en realidad está cargando direcciones de pila no inicializadas al azar e imprimirlo.¿Qué pasa si proporcionamos valores explícitamente para las dos estructuras?
Golpea el sonido metálico, oopsie:
Destino similar con g ++ también:
Esto significa que es efectivamente una inicialización directa
bar b(0)
, no una inicialización agregada.Esto probablemente se deba a que si no proporciona una implementación explícita del constructor, esto podría ser un símbolo externo, por ejemplo:
El compilador no es lo suficientemente inteligente como para deducir esto como una llamada no operativa / en línea en una etapa no optimizada.
fuente