Sou parcial em usar listas de inicialização de membros com meus construtores ... mas há muito tempo esqueci as razões por trás disso ...
Você usa listas de inicialização de membros em seus construtores? Se sim, por quê? Se não, por que não?
c++
oop
object-construction
paxos1977
fonte
fonte
Respostas:
Para os alunos da classe POD , não faz diferença, é apenas uma questão de estilo. Para membros da classe que são classes, evita-se uma chamada desnecessária para um construtor padrão. Considerar:
Nesse caso, o construtor for
B
chamará o construtor padrão paraA
e inicializaráa.x
para 3. Uma maneira melhor seria queB
o construtor chamasse diretamenteA
o construtor na lista de inicializadores:Isso chamaria apenas
A
oA(int)
construtor e não o construtor padrão. Neste exemplo, a diferença é insignificante, mas imagine se você quiserA
, o construtor padrão fez mais, como alocar memória ou abrir arquivos. Você não gostaria de fazer isso desnecessariamente.Além disso, se uma classe não tiver um construtor padrão ou se você tiver uma
const
variável de membro, deverá usar uma lista de inicializadores:fonte
Além dos motivos de desempenho mencionados acima, se sua classe armazena referências a objetos passados como parâmetros de construtor ou se sua classe possui variáveis const, você não tem outra opção, exceto o uso de listas de inicializadores.
fonte
Um motivo importante para o uso da lista de inicializadores de construtores que não é mencionado nas respostas aqui é a inicialização da classe base.
De acordo com a ordem de construção, a classe base deve ser construída antes da classe filho. Sem a lista de inicializadores de construtores, isso é possível se sua classe base tiver um construtor padrão, que será chamado imediatamente antes de inserir o construtor da classe filho.
Mas, se sua classe base tiver apenas construtor parametrizado, você deverá usar a lista de inicializadores de construtores para garantir que sua classe base seja inicializada antes da classe filho.
Inicialização de subobjetos que possuem apenas construtores parametrizados
Eficiência
Usando a lista de inicializadores de construtores, você inicializa seus membros de dados no estado exato que você precisa no seu código, em vez de inicializá-los no estado padrão e depois mudar o estado para o que você precisa no seu código.
Se membros de dados const não estáticos da sua classe tiverem construtores padrão e você não usar a lista de inicializadores de construtores, não poderá inicializá-los no estado pretendido, pois serão inicializados no estado padrão.
Os membros dos dados de referência devem ser inicializados quando o compilador entra no construtor, pois as referências não podem ser apenas declaradas e inicializadas posteriormente. Isso é possível apenas com a lista de inicializadores de construtores.
fonte
Além dos problemas de desempenho, há outro muito importante que eu chamaria de manutenção e extensibilidade de código.
Se um T for POD e você começar a preferir a lista de inicialização, se uma vez T for alterado para um tipo não-POD, não será necessário alterar nada em torno da inicialização para evitar chamadas desnecessárias ao construtor, porque ele já está otimizado.
Se o tipo T tiver construtor padrão e um ou mais construtores definidos pelo usuário e uma vez que você decidir remover ou ocultar o padrão, se a lista de inicialização tiver sido usada, não será necessário atualizar o código se os construtores definidos pelo usuário eles já estão implementados corretamente.
O mesmo com membros const ou membros de referência, digamos que inicialmente T seja definido da seguinte maneira:
Em seguida, você decide qualificar como const, se você usasse a lista de inicialização desde o início, essa era uma mudança de linha única, mas com o T definido como acima, também é necessário cavar a definição do construtor para remover a atribuição:
Não é segredo que a manutenção é muito mais fácil e menos propensa a erros se o código foi escrito não por um "macaco de código", mas por um engenheiro que toma decisões com base em considerações mais profundas sobre o que está fazendo.
fonte
Antes de o corpo do construtor ser executado, todos os construtores para sua classe pai e, em seguida, para seus campos são chamados. Por padrão, os construtores sem argumento são chamados. As listas de inicialização permitem escolher qual construtor é chamado e quais argumentos esse construtor recebe.
Se você possui um campo de referência ou const, ou se uma das classes usadas não possui um construtor padrão, você deve usar uma lista de inicialização.
fonte
Aqui, o compilador segue as etapas a seguir para criar um objeto do tipo MyClass
1. O construtor do tipo é chamado primeiro para "a".
2. O operador de atribuição de “Tipo” é chamado dentro do corpo do construtor MyClass () para atribuir
E, finalmente, o destruidor de "Tipo" é chamado de "a", pois fica fora do escopo.
Agora considere o mesmo código com o construtor MyClass () com a lista de inicializadores
Com a lista de inicializadores, as seguintes etapas são seguidas pelo compilador:
fonte
Apenas para adicionar algumas informações adicionais para demonstrar quanta diferença a lista de inicialização de membros pode causar . Na consulta de soma de intervalo 303 leetcode - Imutável, https://leetcode.com/problems/range-sum-query-immutable/ , em que você precisa construir e inicializar para zerar um vetor com determinado tamanho. Aqui estão duas implementações e comparações de velocidade diferentes.
Sem a lista de inicialização dos membros , para obter AC, isso me custou cerca de 212 ms .
Agora, usando a lista de inicialização de membros , o tempo para obter AC é de cerca de 108 ms . Com este exemplo simples, é óbvio que a lista de inicialização de membros é muito mais eficiente . Toda a medição é do tempo de execução do LC.
fonte
Sintaxe:
Lista de necessidade de inicialização:
no programa acima, quando o construtor da classe é executado, Sam_x e Sam_y são criados. Em seguida, no corpo do construtor, essas variáveis de dados do membro são definidas.
Casos de uso:
Em C, as variáveis devem ser definidas durante a criação. da mesma maneira em C ++, devemos inicializar a variável Const e Reference durante a criação do objeto usando a lista de Inicialização. se fizermos a inicialização após a criação do objeto (dentro do corpo do construtor), obteremos um erro de tempo de compilação.
Objetos membros da classe Sample1 (base) que não possuem construtor padrão
Ao criar um objeto para a classe derivada, que internamente chama o construtor da classe derivada e chama o construtor da classe base (padrão). se a classe base não tiver construtor padrão, o usuário receberá um erro de tempo de compilação. Para evitar, devemos ter ou
O nome do parâmetro do construtor da classe e o membro de dados de uma classe são os mesmos:
Como todos sabemos, a variável local com prioridade mais alta do que a variável global se as duas variáveis tiverem o mesmo nome. Nesse caso, o programa considera o valor "i" {variável do lado esquerdo e direito. ou seja: i = i} como variável local no construtor Sample3 () e variável de membro da classe (i) foi substituída. Para evitar, devemos usar
fonte
Conforme explicado nas Diretrizes Principais do C ++ C.49: Preferir inicialização à atribuição em construtores , evita chamadas desnecessárias para construtores padrão.
fonte