Internamente y sobre el código generado, ¿existe realmente una diferencia entre:
MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}
y
MyClass::MyClass()
{
_capacity=15;
_data=NULL;
_len=0
}
Gracias...
Respuestas:
Suponiendo que esos valores son tipos primitivos, entonces no, no hay diferencia. Las listas de inicialización solo hacen una 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. De hecho, esto puede ser notablemente más rápido.
Necesita usar la lista de inicialización para inicializar miembros constantes, referencias y clase base
Cuando necesite inicializar miembros constantes, referencias y pasar parámetros a constructores de clases 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
};
La clase / estructura de ejemplo (no exhaustiva) contiene una referencia:
struct bb {};
struct aa
{
bb& rb;
aa(bb& b ) : rb(b) {}
};
// usage:
bb b;
aa a(b);
Y ejemplo de inicialización de la clase base que requiere un parámetro (por ejemplo, sin 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) {}
};
_capacity
, _data
y _len
tienen tipos de clase sin constructores predeterminados accesibles?
const
miembro en el cuerpo del constructor, debe usar la lista de inicialización; los no const
miembros pueden inicializarse en la lista de inicialización o en el cuerpo del constructor.
Si. En el primer caso puedes 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
idoneidad 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
las instancias deben inicializarse en la lista de inicializadores o los tipos subyacentes deben proporcionar constructores públicos sin parámetros (lo que hacen los tipos primitivos).
Creo que este enlace http://www.cplusplus.com/forum/articles/17820/ ofrece una excelente explicación, especialmente para aquellos que son nuevos en C ++.
La razón por la que las listas de inicializadores son más eficientes es que dentro del cuerpo del constructor, solo tienen lugar las asignaciones, no la inicialización. Entonces, si está tratando con un tipo no incorporado, el constructor predeterminado para ese objeto ya ha sido llamado antes de ingresar el cuerpo del constructor. Dentro del cuerpo del constructor, estás asignando un valor a ese objeto.
En efecto, esta es una llamada al constructor predeterminado seguida de una llamada al operador de asignación de copia. La lista de inicializadores le permite llamar directamente al constructor de copia, y esto a veces puede ser significativamente más rápido (recuerde que la lista de inicializadores está antes del cuerpo del constructor)
Agregaré que si tiene miembros de tipo de clase sin un constructor predeterminado disponible, la inicialización es la única forma de construir su clase.
Una gran diferencia es que la asignación puede inicializar miembros de una clase principal; el inicializador solo funciona en miembros declarados en el ámbito de clase actual.
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.
La verdadera diferencia se reduce a cómo el compilador gcc genera código de máquina y coloca la memoria. Explique:
Ciertamente, hay otras formas de manejar miembros de tipo constante. Pero para facilitarles la vida, los escritores del compilador de gcc deciden establecer algunas reglas
Solo hay una forma de inicializar instancias de clase base y variables miembro no estáticas y 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, entonces ese miembro o base se inicializará por defecto (si el miembro / base es un tipo de clase que no es POD o una matriz de clase que no es POD tipos) o dejar sin inicializar de otra manera.
Una vez que se ingresa el cuerpo del constructor, todas las bases o miembros se habrán inicializado o dejado sin inicializar (es decir, tendrán un valor indeterminado). No hay ninguna oportunidad en el cuerpo del constructor para influir en cómo se deben inicializar.
Es posible que pueda asignar nuevos valores a miembros en el cuerpo del constructor, pero no es posible asignar a const
miembros o miembros de tipo de clase que se han hecho no asignables y no es posible volver a vincular referencias.
Para tipos incorporados y algunos tipos definidos por el usuario, la asignación en el cuerpo del constructor puede tener exactamente el mismo efecto que inicializar con el mismo valor en la lista de inicializadores.
Si no nombra un miembro o una base en una lista de inicializadores y esa entidad es una referencia, tiene un tipo de clase sin un constructor predeterminado declarado por el usuario accesible, está const
calificado y tiene un tipo POD o es un tipo de clase POD o matriz de tipo de clase POD que contiene un const
miembro calificado (directa o indirectamente), el programa está mal formado.
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 la declaración y otro para asignar el valor.
Existe 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 usa 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 un caso posterior, puede haber un problema de regresión cuando alguien agregó código en un constructor antes de que se inicialice un miembro determinado.
Aquí hay un punto que no vi a otros referirse a él:
class temp{
public:
temp(int var);
};
La clase temporal no tiene un ctor predeterminado. Cuando lo usamos en otra clase de la siguiente manera:
class mainClass{
public:
mainClass(){}
private:
int a;
temp obj;
};
el código no se compilará, porque el compilador no sabe cómo inicializar obj
, porque solo tiene un ctor explícito que recibe un valor int, por lo que tenemos que cambiar el ctor de la siguiente manera:
mainClass(int sth):obj(sth){}
Por lo tanto, no se trata solo de constantes y referencias.