Neste caso específico, há uma diferença entre usar uma lista de inicializadores de membros e atribuir valores em um construtor?

Internamente e sobre o código gerado, existe realmente uma diferença entre:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0) { } 

e

 MyClass::MyClass() { _capacity=15; _data=NULL; _len=0 } 

obrigado…

Supondo que esses valores sejam tipos primitivos, então não, não há diferença. As listas de boot só fazem diferença quando você tem objects como membros, pois em vez de usar a boot padrão seguida de atribuição, a lista de boot permite inicializar o object com seu valor final. Isso pode ser notavelmente mais rápido.

Você precisa usar a lista de boot para inicializar membros constantes, referências e class base

Quando você precisar inicializar o membro constante, as referências e os parâmetros de passagem para os construtores da class base, conforme mencionado nos comentários, será necessário usar a lista de boot.

 struct aa { int i; const int ci; // constant member aa() : i(0) {} // will fail, constant member not initialized }; struct aa { int i; const int ci; aa() : i(0) { ci = 3;} // will fail, ci is constant }; struct aa { int i; const int ci; aa() : i(0), ci(3) {} // works }; 

Exemplo (não exaustivo) class / struct contém referência:

 struct bb {}; struct aa { bb& rb; aa(bb& b ) : rb(b) {} }; // usage: bb b; aa a(b); 

E exemplo de inicializar a class base que requer um parâmetro (por exemplo, nenhum construtor padrão):

 struct bb {}; struct dd { char c; dd(char x) : c(x) {} }; struct aa : dd { bb& rb; aa(bb& b ) : dd('a'), rb(b) {} }; 

Sim. No primeiro caso, você pode declarar _capacity , _data e _len como constantes:

 class MyClass { private: const int _capacity; const void *_data; const int _len; // ... }; 

Isso seria importante se você quiser garantir a constância dessas variables ​​de instância ao calcular seus valores em tempo de execução, por exemplo:

 MyClass::MyClass() : _capacity(someMethod()), _data(someOtherMethod()), _len(yetAnotherMethod()) { } 

Instâncias const devem ser inicializadas na lista inicializadora ou os tipos subjacentes devem fornecer construtores públicos sem parâmetros (que tipos primitivos fazem).

Acrescentarei que se você tiver membros do tipo de class sem um construtor padrão disponível, a boot será a única maneira de construir sua class.

Eu acho que este link http://www.cplusplus.com/forum/articles/17820/ dá uma excelente explicação – especialmente para aqueles que são novos em C ++.

A razão pela qual as listas de iniciadores são mais eficientes é que, dentro do corpo do construtor, apenas as atribuições ocorrem, não a boot. Portanto, se você estiver lidando com um tipo não interno, o construtor padrão para esse object já foi chamado antes de o corpo do construtor ser inserido. Dentro do corpo do construtor, você está atribuindo um valor a esse object.

Na verdade, isso é uma chamada para o construtor padrão seguido por uma chamada para o operador de atribuição de cópia. A lista de inicializadores permite que você chame o construtor de cópias diretamente, e isso pode ser significativamente mais rápido (lembre-se de que a lista de inicializadores é anterior ao corpo do construtor)

Uma grande diferença é que a atribuição pode inicializar membros de uma class pai; o inicializador só funciona em membros declarados no escopo atual da class.

Depende dos tipos envolvidos. A diferença é semelhante entre

 std::string a; a = "hai"; 

e

 std::string a("hai"); 

onde a segunda forma é a lista de boot, isto é, faz diferença se o tipo requer argumentos de construtor ou é mais eficiente com argumentos de construtor.

A diferença real resume-se a como o compilador gcc gera código de máquina e estabelece a memory. Explicar:

  • (phase1) Antes do corpo do init (incluindo a lista de boot): o compilador aloca a memory requerida para a class. A turma já está viva!
  • (phase2) No corpo do init: como a memory é alocada, toda atribuição agora indica uma operação na variável já existente / ‘initialized’.

Existem certamente outras maneiras de lidar com membros do tipo const. Mas para facilitar sua vida, os escritores do compilador gcc decidem criar algumas regras

  1. Os membros do tipo const devem ser inicializados antes do corpo do init.
  2. Após a fase 1, qualquer operação de gravação será válida apenas para membros não constantes.

Há apenas uma maneira de inicializar as instâncias da class base e as variables ​​de membro não estático e que está usando a lista de inicializadores.

Se você não especificar uma variável de membro base ou não-estático na lista de inicializadores do construtor, esse membro ou base será inicializado por padrão (se o membro / base for um tipo de class não-POD ou uma matriz de class não-POD) tipos) ou deixados não inicializados de outra forma.

Uma vez que o corpo do construtor é inserido, todas as bases ou membros serão inicializados ou deixados não inicializados (ou seja, terão um valor indeterminado). Não há oportunidade no corpo do construtor para influenciar como eles devem ser inicializados.

Você pode ser capaz de atribuir novos valores aos membros no corpo do construtor, mas não é possível atribuir aos membros de const ou membros do tipo de class que foram feitos não-atribuíveis e não é possível religar as referências.

Para tipos incorporados e alguns tipos definidos pelo usuário, a atribuição no corpo do construtor pode ter exatamente o mesmo efeito que a boot com o mesmo valor na lista de inicializadores.

Se você falhar em nomear um membro ou base em uma lista inicializadora e essa entidade for uma referência, tem tipo de class sem construtor padrão declarado pelo usuário acessível, é const qualificado e tem tipo POD ou é um tipo de class POD ou matriz da class POD tipo contendo um membro qualificado const (direta ou indiretamente), então o programa é mal formado.

Se você escrever uma lista de inicializadores, fará tudo em uma etapa; Se você não escrever uma lista de initilizer, você fará 2 passos: um para declaração e outro para atribuir o valor.

Há uma diferença entre a lista de boot e a instrução de boot em um construtor. Vamos considerar abaixo o código:

 #include  #include  #include  #include  class MyBase { public: MyBase() { std::cout << __FUNCTION__ << std::endl; } }; class MyClass : public MyBase { public: MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) { std::cout << __FUNCTION__ << std::endl; } private: int _capacity; int* _data; int _len; }; class MyClass2 : public MyBase { public: MyClass2::MyClass2() { std::cout << __FUNCTION__ << std::endl; _capacity = 15; _data = NULL; _len = 0; } private: int _capacity; int* _data; int _len; }; int main() { MyClass c; MyClass2 d; return 0; } 

Quando MyClass é usado, todos os membros serão inicializados antes da primeira instrução em um construtor ser executada.

Mas, quando MyClass2 é usado, todos os membros não são inicializados quando a primeira instrução em um construtor é executada.

No caso posterior, pode haver um problema de regressão quando alguém adiciona algum código em um construtor antes que um determinado membro seja inicializado.