Pregunta

Soy parcial a la utilización de listas de inicialización miembros con mis constructores ... pero he olvidado hace tiempo las razones detrás de esto ...

¿Utiliza listas de inicialización miembros en sus constructores? Si es así, ¿por qué? ¿Si no, porque no?

¿Fue útil?

Solución

En miembros de la clase POD , no hace ninguna diferencia, es sólo una cuestión de estilo. Para miembros de la clase, es decir clases, entonces se evita una llamada innecesaria a un constructor por defecto. Considere lo siguiente:

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

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

En este caso, el constructor de B se llama al constructor por defecto para A y, a continuación, inicializar a.x a 3. Una mejor manera sería que el constructor de B para llamar directamente al constructor de A en la lista de inicialización:

B()
  : a(3)
{
}

Esto sólo sería llamada al constructor de A A(int) y no su constructor por defecto. En este ejemplo, la diferencia es insignificante, pero imagina si va constructor por defecto de ese A hizo más, como la asignación de memoria o abrir archivos. Usted no quiere hacer eso de forma innecesaria.

Por otra parte, si una clase no tiene un constructor por defecto, o si tiene una variable miembro const, que debe utilizar una lista de inicialización:

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;
};

Otros consejos

Además de las razones de rendimiento mencionados anteriormente, si el dispositivo almacena las referencias a los objetos de la clase pasa como parámetros del constructor o de su clase tiene las variables const entonces usted no tiene ninguna opción excepto el uso de listas de inicializador.

  1. inicialización de clase base

Una razón importante para utilizar la lista de inicialización del constructor que no se menciona en las respuestas aquí es la inicialización de la clase base.

De acuerdo con la orden de la construcción, de la clase base debe construirse antes de la clase niño. Sin lista de inicialización del constructor, esto es posible si la clase base tiene constructor por defecto que se llama justo antes de entrar en el constructor de la clase hija.

Sin embargo, si la clase base tiene solamente constructor parametrizado, entonces debe usar lista de inicialización del constructor para asegurar que su clase base se inicia antes de la clase de niños.

  1. Inicialización de subobjetos que sólo tienen constructores parametrizados

  2. Eficiencia

Uso de lista de inicialización del constructor, inicializar los miembros de sus datos a estado exacto que necesita en su código en lugar de la primera inicialización a su estado predeterminado y luego cambiar su estado para el que necesita en su código.

  1. Inicialización de miembros de datos const no estáticos

Si los miembros de datos const no estáticos en su clase tienen constructores por defecto y que no utilizan lista de inicialización del constructor, usted no será capaz de inicializarlas a estado previsto, ya que serán inicializados a su estado predeterminado.

  1. Inicialización de miembros de datos de referencia

Los miembros de datos de referencia deben ser inicializado cuando entra compilador constructor como referencias no pueden ser simplemente declaradas y inicializan después. Esto es posible sólo con la lista de inicialización del constructor.

Al lado de los problemas de rendimiento, hay otra muy importante, que yo llamaría de mantenimiento de código y extensibilidad.

Si T es un POD y empezar prefiriendo lista de inicialización, entonces si una vez T cambiará a un tipo no-POD, no será necesario cambiar nada alrededor de inicialización para evitar constructor de llamadas innecesarias debido a que ya se ha optimizado.

Si tipo T tiene constructor por defecto y uno o más constructores definidos por el usuario y una vez que se decide quitar u ocultar la opción por defecto, entonces, si se utiliza la lista de inicialización, que no es necesario actualizar el código por el usuario si su constructores definido debido a que ya se aplican correctamente.

Lo mismo con los miembros o miembros de referencia constante, digamos que inicialmente T se define de la siguiente manera:

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

A continuación, decidir para calificar a una como const, si desea utilizar la lista de inicialización desde el principio, entonces este era un solo cambio de línea, pero después de la T define como anteriormente, sino que también requiere que cavar la definición del constructor para quitar la asignación :

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

No es un secreto que el mantenimiento es mucho más fácil y menos propenso a errores si el código no fue escrito por un "mono código" sino por un ingeniero que toma decisiones sobre la base de una consideración más profunda de lo que está haciendo.

Antes de que el cuerpo del constructor se ejecuta, todos los constructores de su clase padre y luego por sus campos se invocan. Por defecto, los constructores sin argumentos se invocan. listas de inicialización le permiten elegir qué constructor se llama y qué argumentos que recibe el constructor.

Si usted tiene una referencia o un campo const, o si una de las clases utilizadas no tener un constructor predeterminado, debe utilizar una lista de inicialización.

// 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;
    }
};

Aquí compilador sigue los siguientes pasos para crear un objeto de tipo MiClase
     1. Tipo de constructor es llamado por primera vez para “a”.
    2. El operador de asignación de “Tipo” se llama el interior del cuerpo de MiClase () constructor para asignar

variable = a;
  1. Y, por último destructor de “Tipo” se llama de “a”, ya que se sale del ámbito.

    Ahora considere el mismo código con MiClase () constructor lista de inicialización

    // 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
    }
    };
    

    Con la lista de inicialización, siga estos pasos son seguidos por el compilador:

    1. Copiar constructor de la clase “Tipo” se llama para inicializar: (a) variable. Los argumentos a lista de inicialización se utilizan para copiar construir “variable” directamente.
    2. Destructor de “Tipo” se llama de “a”, ya que se sale del ámbito.

Sólo para añadir algo de información adicional para demostrar la gran diferencia que la lista de inicialización miembro puede MAK . En el leetcode 303 Rango Suma consultas - Inmutable, https://leetcode.com/problems / gama de suma-consulta-inmutable / , donde tiene que construir e inicializar a cero un vector con cierto tamaño. Aquí es dos diferentes aplicación y la velocidad comparación.

Sin miembro de inicialización lista, para obtener CA Me costó alrededor 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];
}
};

Ahora lista de inicialización utilizando miembro , el tiempo para llegar AC se trata de 108 ms . Con este sencillo ejemplo, es bastante obvio que, lista de miembros de inicialización es la forma más eficiente . Toda la medición es del tiempo de funcionamiento 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];
}
};

Sintaxis:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

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

Necesidad de la lista de inicialización:

 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
  };

En el programa anterior, cuando se ejecuta el constructor de la clase, Sam_x y Sam_y se crean. Luego, en el cuerpo constructor, se definen las variables de datos miembro.

Los casos de uso:

  1. variables de Const y de referencia en una clase

En C, las variables debe definirse durante la creación. de la misma forma en C ++, hay que inicializar la Const y variable de referencia durante la creación de objetos mediante el uso de la lista de inicialización. si hacemos inicialización después de la creación del objeto (cuerpo del constructor interior), obtendremos error de compilación tiempo.

  1. objetos miembro de Sample1 (base) de clase que no tiene constructor por defecto

     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 */
      {
    
      }
     };
    

Si bien la creación de objeto para clase derivada que internamente llamadas derivado constructor de la clase y pide constructor de la clase base (por defecto). si la clase base no tiene constructor por defecto, el usuario obtendrá error de compilación tiempo. Para evitar, debemos tener bien

 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. nombre del parámetro y datos de los miembros del constructor de la clase de una clase son iguales:

     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, la variable local que tiene la más alta prioridad a continuación variable global, si ambas variables están teniendo mismo nombre. En este caso, el programa considera "i" valor de la variable {lado de la izquierda y la derecha. es decir: i = i} como variable local en Sample3 () constructor y variable miembro de clase (i) dieron anulación. Para evitar, hay que utilizar

  1. Initialization list 
  2. this operator.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top