De acordo com a resposta aceita (e apenas) para esta pergunta de estouro de pilha ,
Definindo o construtor com
MyTest() = default;
em vez disso, inicializará o objeto com zero.
Então, por que o seguinte,
#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;
}
produza esta saída:
0 32766
Ambos os construtores definidos são padrão? Certo? E para os tipos de POD, a inicialização padrão é a inicialização zero.
E de acordo com a resposta aceita para esta pergunta ,
Se um membro do POD não for inicializado no construtor nem via inicialização em classe C ++ 11, ele será inicializado por padrão.
A resposta é a mesma, independentemente da pilha ou pilha.
No C ++ 98 (e não posteriormente), o novo int () foi especificado como executando a inicialização zero.
Apesar de tentar envolver minha cabeça (ainda que minúscula ) nos construtores padrão e na inicialização padrão , não consegui encontrar uma explicação.
fonte
bar
O construtor é fornecidofoo
pelo usuário, enquanto o construtor é o padrão.a
é zero.b
não é. Parecea
ser inicializado.bar::bar()
seja visívelmain()
- ela pode ser definida em uma unidade de compilação separada e fazer algo muito trivial, enquantomain()
apenas a declaração é visível. Acho que você concorda que esse comportamento não deve mudar, dependendo se você colocabar::bar()
a definição em uma unidade de compilação separada ou não (mesmo que toda a situação não seja intuitiva).int a = 0;
você quer ser realmente explícito.Respostas:
A questão aqui é bastante sutil. Você pensaria que
daria a você um construtor padrão gerado pelo compilador, mas ele agora é considerado fornecido pelo usuário. [dcl.fct.def.default] / 5 estados:
ênfase minha
Portanto, podemos ver que, como você não assumiu o padrão
bar()
quando o declarou pela primeira vez, agora é considerado fornecido pelo usuário. Por causa disso [dcl.init] /8.2não se aplica mais e não estamos inicializando o valor,
b
mas inicializando o padrão por [dcl.init] /8.1fonte
(*_*)
... Se, mesmo para usar as construções básicas da linguagem, preciso ler as letras miúdas do rascunho da linguagem, então Aleluia! Mas provavelmente parece ser o que você diz.bar::bar() = default
fora de linha é o mesmo que fazerbar::bar(){}
inline.A diferença de comportamento vem do fato de que, de acordo com
[dcl.fct.def.default]/5
,bar::bar
é fornecido pelo usuário ondefoo::foo
não é 1 . Como consequência,foo::foo
irá valorizar-inicializar seus membros (ou seja: de zero-inicializarfoo::a
), masbar::bar
permanecerá não inicializado 2 .1)
[dcl.fct.def.default]/5
2)
Da resposta de Vittorio Romeo
fonte
Da cppreference :
Dada essa definição,
foo
é um agregado, enquantobar
não é (possui construtor fornecido pelo usuário e não padronizado).Portanto
foo
, para ,T object {arg1, arg2, ...};
é uma sintaxe para inicialização agregada.Portanto, o
a.a
valor é inicializado, o que paraint
significa inicialização zero.Pois
bar
,T object {};
por outro lado, é a inicialização de valor (da instância da classe, não a inicialização de valor de membros!). Como é um tipo de classe com um construtor padrão, o construtor padrão é chamado. O construtor padrão que você definiu como padrão inicializa os membros (em virtude de não ter inicializadores de membros) que, no caso deint
(com armazenamento não estático), saemb.b
com um valor indeterminado.Não. Isso está errado.
PS Uma palavra sobre seu experimento e sua conclusão: ver que o resultado é zero não significa necessariamente que a variável foi inicializada com zero. Zero é o número perfeitamente possível para um valor de lixo.
O fato de o valor ter sido o mesmo várias vezes também não significa necessariamente que ele foi inicializado.
O fato desse resultado ser o mesmo com várias opções do compilador não significa que a variável seja inicializada. (Embora em alguns casos, a alteração da versão padrão possa alterar se ela foi inicializada).
Não há maneira garantida no C ++ para fazer com que o valor do valor não inicializado apareça diferente de zero.
A única maneira de saber que uma variável é inicializada é comparar o programa com as regras da linguagem e verificar se as regras dizem que ela foi inicializada. Nesse caso,
a.a
é realmente inicializado.fonte
a
é inicializado. Eu estava pensando quea
é inicializado por padrão e a inicialização padrão para um membro POD é, inicialização zero. Éa
então apenas felizmente sempre chegando zero, não importa quantas vezes eu executar este programa.Then how come a is initialized.
Porque é um valor inicializado.I was thinking a is default initialized
Não é.Tentei executar o snippet que você forneceu como
test.cpp
, por meio de gcc & clang e vários níveis de otimização:Então é aí que fica interessante, mostra claramente que o clang O0 build está lendo números aleatórios, presumivelmente espaço na pilha.
Eu rapidamente virei minha IDA para ver o que está acontecendo:
Agora, o que
bar::bar(bar *this)
faz?Hmm, nada. Tivemos que recorrer ao assembly:
Então sim, é apenas, nada, o que o construtor basicamente faz é
this = this
. Mas sabemos que ele está realmente carregando endereços de pilha não inicializados aleatórios e imprimindo-os.E se fornecermos explicitamente valores para as duas estruturas?
Acertar clang, oopsie:
Destino semelhante com o g ++:
Portanto, isso significa que é efetivamente uma inicialização direta
bar b(0)
, não agregada.Provavelmente, porque se você não fornecer uma implementação explícita do construtor, isso poderá ser um símbolo externo, por exemplo:
O compilador não é inteligente o suficiente para deduzir isso como uma chamada in-op / in-line em um estágio não otimizado.
fonte