sobrecarga qué que programacion metodos inicializador implementa ejemplos definicion copia constructores como c++ c++11 initialization

qué - ¿C++ 11 lista de inicializadores de miembros vs inicializador en clase?



que es un inicializador en programacion (3)

¿Qué diferencia hay entre estas formas de inicializar variables de miembro de objeto en C ++ 11? Hay otra manera ? ¿Qué camino es mejor (rendimiento)?

class any { public: obj s = obj("value"); any(){} };

O

class any { public: obj s; any(): s("value"){} };

Gracias.


Ambos ejemplos son equivalentes.
Aunque solo si el tipo es copiable o móvil (verifíquelo usted mismo) y NRVO está realmente hecho (cualquier compilador medio decente lo hará como una cuestión de rutina).

Aunque si tuvieras muchos constructores y el encadenamiento de constructores fuera inapropiado, el primer método te permitiría no repetirte.

Además, puede usar ese método para definir agregados con valores predeterminados diferentes de la inicialización agregada para (algunos) miembros desde C ++ 14.


Ellos son lo mismo.

Ninguno es mejor que el otro en términos de rendimiento, y no hay otra manera de inicializarlos.

El beneficio de la inicialización en clase (el primero en su ejemplo) es que el orden de inicialización es implícito. En la lista de inicializadores, debe indicar explícitamente el pedido, y los compiladores avisarán de la inicialización fuera de orden si obtiene el pedido incorrecto.

De la norma:

12.6.2.5 nonstatic data members shall be initialized in the order they were declared in the class definition

Si el pedido se equivoca en su lista, GCC se quejará:

main.cpp: In constructor ''C::C()'': main.cpp:51:9: warning: ''C::b'' will be initialized after main.cpp:51:6: warning: ''int C::a''

El beneficio de las listas inicializadoras es quizás una cuestión de gusto: la lista es explícita, normalmente en el archivo fuente. En clase es implícito (podría decirse), y normalmente está en el archivo de encabezado.


No, estos no son los mismos .

La diferencia entre ellos es la misma que se aplica a la inicialización directa frente a la inicialización de la copia , que es sutil pero a menudo muy confusa.

§12.6.2 [class.base.init]:

  1. La lista de expresiones o la lista iniciada con bridas en un inicializador de mem se usa para inicializar el subobjeto designado (o, en el caso de un constructor delegante, el objeto de clase completo) de acuerdo con las reglas de inicialización de 8.5 para la inicialización directa. [...]

  2. En un constructor no delegante, si un miembro de datos no estático o una clase base dados no están designados por un id-inicializador-mem (incluido el caso en el que no hay una lista-inicializador-mem porque el constructor no tiene inicializador-ctor ) y la entidad no es una clase base virtual de una clase abstracta (10.4), entonces

    - si la entidad es un miembro de datos no estáticos que tiene un inicializador de corsé o igual , la entidad se inicializa como se especifica en 8.5 ;

§8.5 [dcl.init]:

  1. La inicialización que se produce en el formulario.

    T x = a;

así como en el paso de argumentos, el retorno de función, el lanzamiento de una excepción (15.1), el manejo de una excepción (15.3) y la inicialización agregada de miembros (8.5.1) se denomina inicialización de copia .

La inicialización de un miembro de datos no estáticos en una lista de inicialización de miembros sigue las reglas de la inicialización directa , que no crea temporarios intermedios que se deben mover / copiar (si se compilan sin una elección de copia ), ni el tipo de el miembro de datos debe poder copiarse / moverse (incluso si la copia es eluida). Además, una inicialización directa introduce un contexto explícito, mientras que una inicialización de copia no es explícita (si un constructor seleccionado para la inicialización es explicit , el programa no compilará).

En otras palabras, el obj s = obj("value"); la sintaxis no se compilará si obj se declara como:

struct obj { obj(std::string) {} obj(const obj&) = delete; };

o:

struct obj { obj(std::string) {} explicit obj(const obj&) {} };

Como un ejemplo más tangible, mientras que el siguiente no compilará:

struct any { std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable std::atomic<int> b = 2; // ill-formed: explicit constructor selected };

este va a:

struct any { std::atomic<int> a; std::atomic<int> b{ 2 }; any() : a(1) {} };

¿Qué camino es mejor (rendimiento)?

Con un copy-elision habilitado ambos tienen un rendimiento idéntico. Con copy-elision desactivado, hay una llamada adicional de constructor de copiar / mover en cada instanciación cuando se usa la sintaxis de inicialización de copia (that obj s = obj("value"); es uno de).

Hay otra manera ?

La sintaxis del iniciador de brace-o-igual le permite a uno realizar una inicialización de lista directa también:

class any { public: obj s{ "value" }; any() {} };

¿Hay otras diferencias?

Algunas otras diferencias que vale la pena mencionar son:

  1. Brace-o-equal-initializer debe residir en un archivo de encabezado junto con una declaración de clase.
  2. Si ambos se combinan, member-initializer-list tiene prioridad sobre brace-o-equal-initializer (es decir, brace-or-equal-initializer) se ignora.
  3. (Solo C ++ 11, hasta C ++ 14) Una clase que usa el inicializador con o sin llave viola las restricciones para un tipo agregado.
  4. Con la sintaxis de inicialización de corsé o igual no es posible realizar una inicialización directa que no sea una inicialización de lista directa .