tipos que programas poo ejemplos ejemplo definicion copia constructores codigo clases avanzados c++ initialization initialization-list

c++ - que - En este caso específico, ¿hay alguna diferencia entre usar una lista de inicializador de miembros y asignar valores en un constructor?



que es un destructor en c++ (11)

Internamente y sobre el código generado, ¿hay realmente una diferencia entre:

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

y

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

Gracias...


Añadiré que si tiene miembros del tipo de clase sin un constructor predeterminado disponible, la inicialización es la única forma de construir su clase.


Creo que este enlace http://www.cplusplus.com/forum/articles/17820/ ofrece una excelente explicación, especialmente para los nuevos en C ++.

La razón por la cual las listas de inicializadores son más eficientes es que dentro del cuerpo constructor, solo las asignaciones tienen lugar, no la inicialización. Entonces, si se trata de un tipo no incorporado, el constructor predeterminado para ese objeto ya ha sido llamado antes de que se haya ingresado el cuerpo del constructor. Dentro del cuerpo del constructor, está asignando un valor a ese objeto.

En efecto, esta es una llamada al constructor predeterminado seguido de una llamada al operador de copia de asignación. La lista de inicializadores le permite llamar al constructor de copias directamente, y esto a veces puede ser significativamente más rápido (recuerde que la lista de inicializadores está antes del cuerpo del constructor)


Depende de los tipos involucrados. La diferencia es similar entre

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

y

std::string a("hai");

donde la segunda forma es la lista de inicialización, es decir, hace una diferencia si el tipo requiere argumentos de constructor o es más eficiente con argumentos de constructor.


Hay una diferencia entre la lista de inicialización y la declaración de inicialización en un constructor. Consideremos el siguiente código:

#include <initializer_list> #include <iostream> #include <algorithm> #include <numeric> 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; }

Cuando se utiliza MyClass, todos los miembros se inicializarán antes de que se ejecute la primera instrucción en un constructor.

Pero, cuando se usa MyClass2, todos los miembros no se inicializan cuando se ejecuta la primera instrucción en un constructor.

En caso posterior, puede haber un problema de regresión cuando alguien agregó algún código en un constructor antes de que se inicialice un determinado miembro.


La diferencia real se reduce a la forma en que el compilador gcc genera código máquina y establece la memoria. Explique:

  • (fase1) Antes del cuerpo init (incluida la lista init): el compilador asigna la memoria requerida para la clase. ¡La clase ya está viva!
  • (fase2) En el cuerpo init: dado que la memoria está asignada, cada asignación ahora indica una operación en la variable ya existente / ''inicializada''.

Ciertamente hay otras formas de manejar los miembros de tipo const. Pero para facilitar su vida, los escritores del compilador de gcc deciden establecer algunas reglas

  1. los miembros del tipo const deben inicializarse antes del cuerpo init.
  2. Después de la fase 1, cualquier operación de escritura solo es válida para miembros no constantes.

Sí. En el primer caso, puede declarar _capacity , _data y _len como constantes:

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

Esto sería importante si desea garantizar la const -ness de estas variables de instancia al calcular sus valores en tiempo de ejecución, por ejemplo:

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

const instancias const deben inicializarse en la lista de inicializadores o los tipos subyacentes deben proporcionar constructores públicos sin parámetros (que tipos primitivos sí).


Si escribe una lista de inicializadores, lo hace todo en un solo paso; si no escribe una lista de iniciadores, realizará 2 pasos: uno para declarar y otro para asignar el valor.


Solo hay una forma de inicializar instancias de clase base y variables de miembro no estáticas, y eso es usar la lista de inicializadores.

Si no especifica una variable de miembro base o no estática en la lista de inicializadores de su constructor, ese miembro o base se inicializará por defecto (si el miembro / base es un tipo de clase no POD o una matriz de clase no POD) tipos) o dejado sin inicializar de lo contrario.

Una vez que se ingresa el cuerpo constructor, todas las bases o miembros se han inicializado o dejado sin inicializar (es decir, tendrán un valor indeterminado). No hay oportunidad en el cuerpo constructor de influir en cómo deben inicializarse.

Puede asignar nuevos valores a los miembros en el cuerpo del constructor, pero no es posible asignar a los miembros de const o miembros del tipo de clase que se han hecho no asignables y no es posible volver a enlazar las referencias.

Para tipos incorporados y algunos tipos definidos por el usuario, la asignación en el cuerpo constructor puede tener exactamente el mismo efecto que la inicialización con el mismo valor en la lista de inicializadores.

Si no puede nombrar un miembro o base en una lista de inicializadores y esa entidad es una referencia, tiene tipo de clase sin constructor predeterminado accesible declarado por el usuario, está const calificado y tiene tipo POD o es un tipo de clase POD o matriz de clase POD tipo que contiene un miembro const calificado (directa o indirectamente), entonces el programa está mal formado.


Suponiendo que esos valores son tipos primitivos, entonces no, no hay diferencia. Las listas de inicialización solo hacen la diferencia cuando tiene objetos como miembros, ya que en lugar de usar la inicialización predeterminada seguida de la asignación, la lista de inicialización le permite inicializar el objeto a su valor final. Esto en realidad puede ser notablemente más rápido.


Una gran diferencia es que la asignación puede inicializar miembros de una clase principal; el inicializador solo funciona en los miembros declarados en el alcance de clase actual.


Necesita utilizar la lista de inicialización para inicializar miembros constantes, referencias y clases base

Cuando necesite inicializar miembro constante, referencias y pasar parámetros a los constructores de la clase base, como se menciona en los comentarios, debe usar la lista de inicialización.

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

Ejemplo (no exhaustivo) clase / estructura contiene referencia:

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

Y un ejemplo de inicialización de la clase base que requiere un parámetro (por ejemplo, ningún constructor predeterminado):

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