A necessidade de inicialização sem construtor
(consteval.ca)Preciso inicializar sem construtor
-
Introdução
- Ao aprender C++ pela primeira vez, você descobre os casos em que o compilador fornece um construtor padrão.
- Isso leva à preocupação com o risco de um objeto não ser inicializado em certas situações.
-
Inicialização padrão e inicialização por valor
T t;realiza inicialização padrão.- Se
Tfor um tipo de classe e tiver construtor padrão, ele será executado. - Se
Tfor um tipo de array, cada elemento será inicializado por padrão. - Caso contrário, nada acontece.
- Se
T t{};realiza inicialização por valor.- Se
Tfor um tipo de classe, faz inicialização padrão quando não houver construtor padrão ou quando houver um construtor padrão fornecido pelo usuário ou deletado. - Caso contrário, primeiro inicializa com zero e depois faz inicialização padrão.
- Se
Tfor um tipo de array, cada elemento será inicializado por valor. - Caso contrário, inicializa com zero.
- Se
-
Construtor padrão
- Se você não declarar um construtor padrão, o compilador declara um construtor padrão implicitamente.
- O construtor padrão declarado implicitamente tem corpo vazio e lista de inicialização de membros vazia.
- Exemplo:
struct T { int x; T() = default; }; T t{}; std::cout << t.x << std::endl; // resultado da saída é 0
-
Construtor padrão definido implicitamente
- Se o construtor padrão for declarado implicitamente ou declarado explicitamente como default, o compilador fornece um construtor padrão definido implicitamente.
- Exemplo:
struct T { T(); }; T::T() = default; T t{}; std::cout << t.x << std::endl; // resultado da saída é valor lixo
-
Casos em que não é possível fornecer um construtor padrão
- Quando
Ttem um membro de referência não estático - Quando
Ttem um membro não estático ou uma classe base não abstrata que não pode ser construída por padrão ou destruída - Quando
Ttem um membroconstnão estático sem inicializador de membro padrão
- Quando
-
Inicialização correta
T t{};realiza inicialização por lista.- A inicialização por lista se divide em inicialização direta por lista e inicialização por cópia com lista.
- Exemplo:
struct S { int a; float b; char c; }; S s{3, 4.0f, 'S'}; // nenhuma chamada de construtor
-
Inicialização por lista e inicialização de agregados
- A inicialização de agregados é uma forma especial de inicialização por lista, em que cada elemento da classe ou do array é inicializado por cópia a partir de cada elemento da lista de inicialização.
- Exemplo:
struct A { const int x; }; A a{}; // a.x é inicializado com 0
-
Inicialização com parênteses
- A inicialização com parênteses realiza inicialização direta sem lista.
- Exemplo:
struct T { const int& r; }; T t(42); // t.r é uma referência para 42
-
Resumo
- As regras de inicialização são complexas, mas escrever construtores manualmente evita a maioria dos problemas.
- Em vez de deixar isso para o compilador, é melhor escrever o construtor diretamente.
Opinião do GN⁺
- Este texto explica bem a complexidade das regras de inicialização em C++.
- Entender as regras de inicialização em C++ é importante, pois isso afeta bastante a estabilidade e o desempenho do código.
- Escrever construtores diretamente é a melhor forma de evitar problemas de inicialização.
- Uma linguagem com funcionalidade semelhante é Rust, que tem regras de inicialização mais claras.
- Ao adotar novas tecnologias, é importante compreender e usar corretamente detalhes como as regras de inicialização.
1 comentários
Comentários no Hacker News
O resultado da inicialização de
tserá 0té inicializado por valor e, comoTnão tem um construtor padrão definido pelo usuário, o objeto é zerado antes de o construtor padrão ser chamadoO construtor padrão inicializa os membros por padrão, o que é diferente de inicialização por valor
O GCC parece concordar com isso
O autor deixou passar que na verdade está inicializando
xpor valorOs detalhes das regras são complexos e às vezes têm partes irracionais
Tornar a inicialização padrão explicitamente possível seria uma grande melhoria
std::array<int, 100> = void;seria melhorA ligação entre inicialização por lista e inicialização de agregados é que, quando a inicialização por lista é feita sobre um agregado, a inicialização de agregado é realizada
O caso de um elemento funciona de forma diferente de dois ou mais elementos
É possível escrever o próprio construtor e inicializar uma tupla ou array com apenas um elemento fornecido
Quando a lista de inicialização do C++11 apareceu pela primeira vez, descobrir isso pareceu loucura
Menção a "I Have No Mouth, and I Must Scream" (1967)
Uso da sintaxe
T::T() = default;Espera-se que a saída seja 0, mas na prática sairá um valor lixo
Permite que consumidores da biblioteca alterem o comportamento da biblioteca
Se quiser ainda mais complexidade de C++, a recomendação é o C++ FQA
O tema do blog foi inspirado em computadores da era DEC, mas é limpo e minimalista
Ler isso dá uma sensação de tontura
Go e Rust não têm construtores especiais, o que simplifica muita coisa
Fica a curiosidade se existe alguma ferramenta de C++ que mostre todos os comportamentos implícitos
Há informação incorreta sobre a classe
A afirmação de que "
T t;não faz nada" está erradaT t;falhaO cabeçalho do blog tem um painel frontal da DEC