Beneficios de las listas de inicialización

De lo que sé de los beneficios de utilizar la lista de inicialización es que proporcionan eficiencia al inicializar los miembros de la clase que no están integrados. Por ejemplo,

Fred::Fred() : x_(whatever) { }

es preferible a,

Fred::Fred() { x_ = whatever; }

si x es un objeto de una clase personalizada. Aparte de eso, este estilo se utiliza incluso con tipos incorporados en aras de la coherencia.

El beneficio más común de hacer esto es un mejor rendimiento. Si la expresión sea del mismo tipo que la variable miembro x_, el resultado de cualquier expresión se construye directamente dentro de x_ – el comstackdor no hace una copia separada del objeto.

Con el otro estilo, la expresión cualquiera causa que se cree un objeto temporal separado, y este objeto temporal se pasa al operador de asignación del objeto x_. Entonces ese objeto temporal es destruido en el; Eso es ineficiente.

Pregunta
¿Hay alguna ganancia de eficiencia en el siguiente ejemplo con el uso de la lista de inicialización? Creo que no hay ganancia. La primera versión llama al constructor de copia de cadena y al operador de asignación de cadena de otras llamadas (no se crea ninguna temporal). ¿Correcto?

 class MyClass { public: MyClass(string n):name(n) { } private: string name; }; class MyClass { public: MyClass(string n) { name=n; } private: string name; }; 

La segunda versión llama al ctor predeterminado de string y luego al operador de copia-asignación de cadena; definitivamente podría haber pérdidas de eficiencia (menores) en comparación con la primera, lo que llama directamente c’s copy-ctor (por ejemplo, dependiendo de la implementación de la cadena, puede haber asignación inútil-luego-liberación de alguna estructura pequeña). ¿Por qué no solo usar siempre de la manera correcta? -)

Creo que la única forma de inicializar los miembros de datos de const está en la lista de inicialización

P.ej. en el encabezado:

 class C { C(); private: const int x; int y; } 

Y el en el archivo cpp:

 C::C() : x( 10 ), y( 10 ) { x = 20; // fails y = 20; } 

Es una excelente forma de inicializar miembros que:

  • son const
  • no tiene un constructor predeterminado (es privado)

Recuerde que hay una clara diferencia entre un constructor de copia y un operador de asignación:

  • el copiador construye un nuevo objeto usando alguna otra instancia como un lugar para obtener información de inicialización.
  • el operador de asignación modifica un objeto ya existente que ya se ha construido por completo (incluso si solo está utilizando un constructor predeterminado)

Por lo tanto, en su segundo ejemplo, ya se ha hecho algún trabajo para crear un name en el momento en que

  name=n; 

es alcanzado.

Sin embargo, es bastante posible (especialmente en este ejemplo simple) que el trabajo realizado sea extremadamente pequeño (probablemente solo ponga en cero a algunos miembros de datos en el objeto de string ) y que el trabajo se optimice completamente en una comstackción optimizada. pero se considera buena forma de usar listas de inicializadores siempre que sea posible.

A continuación se muestran los escenarios cuando se usa la lista de inicializadores:

  1. Para la inicialización de miembros de datos de const no estáticos.
  2. Para la inicialización de los miembros de referencia.
  3. Para la inicialización de objetos miembros que no tienen un constructor predeterminado.
  4. Para la inicialización de los miembros de la clase base.
  5. Cuando el nombre del parámetro del constructor es el mismo que el miembro de datos.
  6. Por razones de rendimiento.

También podemos realizar la delegación del constructor a través de la lista de inicialización.