2009-03-31 27 views
6

he estado programando en C++ un tiempo y he utilizado dos métodos:Inicializando en constructores, ¿práctica recomendada?

class Stuff { 
public: 
    Stuff(int nr) : n(nr) { } 
private: 
    int n; 
} 

O

class Stuff { 
public: 
    Stuff(int nr) { 
     n = nr; 
    } 
private: 
    int n; 
} 

Nota: Esto no es lo mismo que this, similar pero no igual.

¿Qué se considera la mejor práctica?

Respuesta

21

Se prefieren las listas de inicializadores. Consulte FAQ 10.6

+2

Obtener una prohibición en ese enlace. Supongo que esta es la razón por la que se desaconsejan las respuestas de solo enlace. – wvdz

+0

Aquí está el enlace actual que explica por qué se prefieren las listas de inicializadores. https://isocpp.org/wiki/faq/ctors#init-lists – NigoroJr

5

Utilice la lista de inicializadores cuando sea posible. Para un int, no importa mucho de cualquier manera, pero para un objeto miembro más complejo, terminarías con el constructor predeterminado del objeto al que se llama, seguido de una asignación a ese objeto, que probablemente terminará siendo más lento

Además, tiene que hacerlo de todas formas para los miembros o miembros de const que no tienen un constructor predeterminado.

+2

También se requiere el uso de la lista de inicio para los miembros de referencia. –

2

Si es posible, utilice la primera versión.

El primero es inicializar utilizando listas de inicializadores, y realmente llama a los constructores de los miembros.

El segundo es asignación. Si n fuera de un tipo con un constructor predeterminado, ya se lo habría llamado y luego se le asignaría. Si n no tiene un constructor predeterminado, se vería obligado a usar el primer tipo. Del mismo modo, si n era una referencia: int &n.

Si no hay constructores de sus miembros que directamente lleven uno de los parámetros a su constructor, puede valer la pena agregar funciones privadas estáticas que puedan hacer la conversión por usted.

1

Generalmente intento hacer la lista de inicializadores cuando puedo. Por un lado, esto hace que sea explícito que estás inicializando el código en el constructor. const memebers tiene para ser inicializado de esta manera.

Si simplemente pones código en el cuerpo del constructor, es muy posible que alguien decida venir y mover una gran parte de él en una rutina de "configuración" sin constructor más adelante.

Sin embargo, se puede tomar por la borda. Tengo un compañero de trabajo al que le gusta crear clases que tienen 2 páginas de código de iniciador, sin código de constructor y quizás 2 páginas para el resto del código de la clase. Encuentro eso realmente difícil de leer.

0

La segunda opción no es la inicialización sino la asignación. Con tipos que tienen constructores predeterminados definidos por el usuario, la segunda opción llamará al constructor predeterminado y más tarde llamará al operador de asignación (definido o no por el usuario) para asignar el valor.

Algunos tipos no pueden inicializarse por defecto: si tiene un atributo sin constructor predeterminado, mantiene referencias (constantes o no) o tiene atributos constantes, debe inicializarlos en la lista de inicializadores.

Las matrices pueden ser de valor inicializado en la lista de inicialización, pero no en el cuerpo del constructor:

class X { 
public: 
    X() : array() {} // value-initializes the array 
// equivalent to: 
// X() { for (int i = 0; i < 10; ++i) array[i]=0; }  
private: 
    int array[10]; 
}; 

Para los tipos de POD, se les puede valorar a inicializar en la lista de inicialización, pero no dentro de los corchetes:

class X { 
public: 
    X() : pod() {} // value-initializes 
// equivalent to (but easier to read and subtly faster as it avoids the copy): 
// X() { pod = {}; } 
private: 
    PODType pod; 
}; 

Finalmente, algunas clases ofrecen funcionalidad mediante el uso de constructores que serán más complejos (si es posible) después de la construcción predeterminada.

class X 
{ 
public: 
    X() : v(10) {} // construct a vector of 10 default initialized integers 
// equivalent to: 
// X() { for (int i = 0; i < 10; ++i) v.push_back(0); } 
private: 
    std::vector<int> v; 
}; 

Por último, cada vez que son equivalentes, las listas de inicialización son más idiomáticas en C++.

12

Una gran ventaja para usar los inicializadores: si se lanza una excepción en cualquier lugar dentro de la lista de inicializadores, se invocarán los destructores para los miembros que ya se han inicializado, y solo para esos miembros.

Cuando utiliza el cuerpo contructor para inicializar el objeto, depende de usted manejar las excepciones correctamente y desenrollar el objeto según corresponda. Esto suele ser mucho más difícil de hacer bien.

0

Quiero agregar que no necesita declarar la lista de inicializadores en el encabezado (.h). Se puede hacer en la implementación del constructor (que es muy común).

Entonces:

//Stuff.h 
class Stuff { 
public: 
    Stuff(int nr); 
private: 
    int n; 
} 

//Stuff.cpp 
Stuff::Stuff(int nr) 
: n(nr) 
{ 
    //initalize complex members 
} 

es legal y imo concentra la inicialización de los campos donde importa. A veces necesitamos inicializar miembros complejos en el cuerpo, por lo que tiene su lista de inicializadores y la inicialización compleja, todo en el archivo .cpp.

Cuestiones relacionadas