Pergunta

Eu sou parcial para usar listas de inicialização membros com meus construtores ... mas não tenho muito tempo desde esquecido as razões por trás disso ...

Você usa listas de inicialização de membros em seus construtores? Se sim, porquê? Se não, por que não?

Foi útil?

Solução

Para os membros da classe POD , não faz diferença, é apenas uma questão de estilo. Para os membros da classe que são as classes, em seguida, ele evita uma ligação desnecessária a um construtor padrão. Considere o seguinte:

class A
{
public:
    A() { x = 0; }
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};

Neste caso, o construtor para B irá chamar o construtor padrão para A e a.x então inicializar a 3. A melhor maneira seria para o construtor de B ao construtor de A diretamente chamada na lista inicializador:

B()
  : a(3)
{
}

Isso só iria chamar o construtor A de A(int) e não seu construtor padrão. Neste exemplo, a diferença é insignificante, mas imagine se você vai construtor padrão desse A fez mais, como alocação de memória ou abrir arquivos. Você não iria querer fazer isso desnecessariamente.

Além disso, se a classe não tem um construtor padrão, ou você tem uma variável membro const, você deve usar uma lista de inicializador:

class A
{
public:
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B() : a(3), y(2)  // 'a' and 'y' MUST be initialized in an initializer list;
    {                 // it is an error not to do so
    }
private:
    A a;
    const int y;
};

Outras dicas

Para além das razões de desempenho mencionado acima, se os seus classe armazena referências a objetos passados ??como parâmetros do construtor ou a sua classe tem const variáveis, então você não tem escolha a não ser usando initializer listas.

  1. Inicialização de classe base

Uma razão importante para a utilização de construtor initializer lista que não é mencionado em respostas aqui é inicialização de classe base.

De acordo com a ordem de construção, classe base deve ser construída antes da aula criança. Sem construtor initializer lista, isso é possível se sua classe base tem construtor padrão que será chamado antes de entrar no construtor da classe filha.

Mas, se sua classe base só tem parametrizado construtor, então você deve usar o construtor initializer lista para garantir que sua classe base é inicializado antes da aula criança.

  1. Inicialização de Subobjects que só tem parametrizado construtores

  2. Eficiência

Usando o construtor initializer lista, você inicializa seus membros de dados para o estado exato que você precisa em seu código em vez de primeira inicialização-los ao seu estado padrão e, em seguida, mudar o seu estado para o que você precisa em seu código.

  1. Inicializar membros de dados const não-estáticos

Se os membros de dados const não-estático em sua classe tem construtores padrão e você não usar o construtor initializer lista, você não será capaz de inicializar-los para o estado pretendido como eles serão inicializados para seu estado padrão.

  1. Inicialização de referência membros de dados

membros de dados de referência deve ser inicializada quando compilador entra construtor como referências não podem ser simplesmente declarou & inicializado depois. Isso só é possível com construtor initializer lista.

Ao lado dos problemas de desempenho, há um outro muito importante, que eu chamaria de manutenção de código e capacidade de extensão.

Se um T é POD e você começa preferindo lista de inicialização, em seguida, se um tempo T irá mudar para um tipo não-POD, você não precisa mudar nada em torno de inicialização para evitar chamadas do construtor desnecessárias porque já está otimizado.

Se o tipo T tem construtor padrão e um ou mais construtores definidos pelo usuário e uma vez que você decidir remover ou ocultar o padrão, em seguida, se a lista de inicialização foi usado, você não precisa de código de atualização se o seu user- construtores definidos, porque eles já estão implementadas corretamente.

Mesmo com membros const ou membros de referência, digamos que inicialmente T é definido da seguinte forma:

struct T
{
    T() { a = 5; }
private:
    int a;
};

Em seguida, você decide para qualificar um como const, se você usaria lista de inicialização desde o início, então esta foi uma única mudança de linha, mas ter o T definido como acima, também exige a cavar a definição construtor para remover atribuição :

struct T
{
    T() : a(5) {} // 2. that requires changes here too
private:
    const int a; // 1. one line change
};

Não é um segredo que a manutenção é muito mais fácil e menos propenso a erros se o código não foi escrito por um "macaco código", mas por um engenheiro que toma decisões com base em consideração mais profunda sobre o que ele está fazendo.

Antes do corpo do construtor é executado, todos os construtores para sua classe pai e, em seguida, para os seus campos são invocados. Por padrão, os construtores sem argumentos são invocados. Inicialização listas permitem que você escolha qual construtor é chamado e quais argumentos que construtor recebe.

Se você tem uma referência ou um campo const, ou se uma das classes utilizadas não tem um construtor padrão, você deve usar uma lista de inicialização.

// Without Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a) {  // Assume that Type is an already
                     // declared class and it has appropriate 
                     // constructors and operators
        variable = a;
    }
};

Aqui compilador segue os seguintes passos para criar um objeto do tipo MyClass
1. construtor do tipo é chamado pela primeira vez para “a”.
2. O operador de atribuição de “Type” é chamado dentro do corpo de MyClass () construtor para atribuir

variable = a;
  1. E então, finalmente destruidor de “Type” é chamado de “a”, uma vez que sai do escopo.

    Agora considere o mesmo código com MyClass () construtor com lista Initializer

    // With Initializer List
     class MyClass {
    Type variable;
    public:
    MyClass(Type a):variable(a) {   // Assume that Type is an already
                     // declared class and it has appropriate
                     // constructors and operators
    }
    };
    

    Com a Lista Initializer, seguindo os passos são seguidos por compilador:

    1. Copiar construtor da classe “Type” é chamado para inicializar: variable (a). Os argumentos em initializer lista são usados ??para copiar construção “variável” diretamente.
    2. Destructor de “Type” é chamado de “a”, uma vez que sai do escopo.

Só para acrescentar algumas informações adicionais para demonstrar quanta diferença a lista de inicialização membro pode mak . No leetcode 303 Faixa Sum Consulta - imutável, https://leetcode.com/problems / intervalo de soma-query-imutável / , onde você precisa para construir e inicializar a zero um vetor com determinado tamanho. Aqui é dois comparação implementação e velocidade diferente.

Sem membro inicialização lista, para obter AC me custar cerca de 212 ms .

class NumArray {
public:
vector<int> preSum;
NumArray(vector<int> nums) {
    preSum = vector<int>(nums.size()+1, 0);
    int ps = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        ps += nums[i];
        preSum[i+1] = ps;
    }
}

int sumRange(int i, int j) {
    return preSum[j+1] - preSum[i];
}
};

Agora lista de inicialização usando membro , o tempo para obter AC é de cerca de 108 ms . Com este exemplo simples, é bastante óbvio que, lista de inicialização membro é a maneira mais eficiente . Toda a medição é do tempo de execução de LC.

class NumArray {
public:
vector<int> preSum;
NumArray(vector<int> nums) : preSum(nums.size()+1, 0) { 
    int ps = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        ps += nums[i];
        preSum[i+1] = ps;
    }
}

int sumRange(int i, int j) {
    return preSum[j+1] - preSum[i];
}
};

Sintaxe:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

     Sample(): Sam_x(1), Sam_y(2)     /* Classname: Initialization List */
     {
           // Constructor body
     }
  };

Necessidade de lista de inicialização:

 class Sample
 {
     public:
         int Sam_x;
         int Sam_y;

     Sample()     */* Object and variables are created - i.e.:declaration of variables */*
     { // Constructor body starts 

         Sam_x = 1;      */* Defining a value to the variable */* 
         Sam_y = 2;

     } // Constructor body ends
  };

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 membro são definidos.

Os casos de uso:

  1. const variáveis ??e Referência em um Class

Em C, as variáveis ?? deve ser definida durante a criação. da mesma forma em C ++, é preciso inicializar a variável Const e referência durante a criação do objeto usando lista de inicialização. se fizermos a inicialização após a criação do objeto (Dentro corpo do construtor), teremos de erro tempo de compilação.

  1. Membro objetos de Sample1 classe (base) que não tem padrão construtor

     class Sample1 
     {
         int i;
         public:
         Sample1 (int temp)
         {
            i = temp;
         }
     };
    
      // Class Sample2 contains object of Sample1 
     class Sample2
     {
      Sample1  a;
      public:
      Sample2 (int x): a(x)      /* Initializer list must be used */
      {
    
      }
     };
    

Ao criar objeto de classe derivada que internamente chamadas derivado construtor da classe e chama o construtor da classe base (padrão). se a classe base não tem construtor padrão, o usuário receberá o erro de tempo de compilação. Para evitar, temos de ter tanto

 1. Default constructor of Sample1 class
 2. Initialization list in Sample2 class which will call the parametric constructor of Sample1 class (as per above program)
  1. nome e dados de parâmetros membro da classe construtor de uma classe são os mesmos:

     class Sample3 {
        int i;         /* Member variable name : i */  
        public:
        Sample3 (int i)    /* Local variable name : i */ 
        {
            i = i;
            print(i);   /* Local variable: Prints the correct value which we passed in constructor */
        }
        int getI() const 
        { 
             print(i);    /*global variable: Garbage value is assigned to i. the expected value should be which we passed in constructor*/
             return i; 
        }
     };
    

Como todos sabemos, variável local ter maior prioridade, em seguida, variável global, se ambas as variáveis ??estão tendo mesmo nome. Neste caso, o programa considera "i" valor {tanto variável lado esquerdo e direito. isto é: i = i} como variável local em Sample3 () construtor e membro da classe variável (i) substituição obtido. Para evitar, temos de usar

  1. Initialization list 
  2. this operator.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top