2011-09-27 14 views
9

Supongamos que tengo:C++: ¿Debo inicializar los miembros del puntero asignados en el cuerpo del constructor a NULL?

// MyClass.h 
class MyClass 
{ 
    public: 
    MyClass(); 

    private: 
    Something *something_; 
} 

// MyClass.cpp 
MyClass::MyClass() 
{ 
    something_ = new Something(); 
} 

¿Debo inicializar something_ a NULL (o 0) en la lista de inicialización del constructor del constructor MiClase? ¿O no es necesario porque lo estoy asignando en el cuerpo del constructor? ¿Cuál es la práctica recomendada?

+8

¿Por qué no inicializar el puntero en el primer lugar? 'MyClass :: MyClass(): something_ (new Something())' –

+0

¿Esa es la mejor práctica? – User

+3

Siempre prefiere la inicialización a la asignación en el cuerpo del constructor. Y use un puntero inteligente como 'shared_ptr' o' scoped_ptr' en lugar de un puntero nativo para que la limpieza sea automática, incluso en el caso de las excepciones. –

Respuesta

11

Generalmente sólo se asigne una vez, en la lista de inicialización o el cuerpo, a menos que la inicialización del cuerpo puede o no puede ocurrir, o tiene código de condición:

MyClass::MyClass() 
{ 
    //this code must happen first 
    // _now_ max is known 
    something_ = new Something(max); 
} 

MyClass::MyClass() 
{ 
    if (max) 
     something_ = new Something(max); 
    else 
     something_ = NULL; 
} 

MyClass::MyClass() 
    : something_(new Something()) 
//as pointed out in the comments, use smart pointers 
{ 
} 
+5

Inicializar un miembro de datos de puntero con el resultado de una asignación dinámica _en la lista de inicialización_ es una mala idea. Muchas cosas pueden salir mal. Por ejemplo, si se lanza una excepción durante la construcción de algún otro miembro de datos, ¿cómo sabe qué miembros de datos se inicializaron para que pueda destruir el objeto asignado dinámicamente? Incluso si usa un bloque try de función, no sabe dónde falló la construcción. (Por supuesto, uno debe usar un puntero inteligente, en cuyo caso la asignación dinámica en la lista de inicialización está bien, pero tenga cuidado al usar punteros comunes). –

+1

@James: Bueno, por supuesto, uno debe usar un puntero inteligente en lugar de un puntero nativo. (Hah, acabas de editar tu comentario para decir eso). –

+0

¿Qué sucede si estoy creando diferentes objetos y el primero requiere el segundo como argumento? ¿Puede usar un inicializador en este caso o es el mismo que su ejemplo máximo? (por ejemplo ': something_ (new Something()), somethingElse_ (new SomethingElse (something _)) ...') – User

2

En general, no. Pero si en algún lugar de su puntero constructor se utiliza antes de que se haya inicializado, entonces obtendrá un comportamiento indefinido. Sin embargo, el mayor problema que tienes es este: si se lanza una excepción en el constructor, no se llama a su destructor. Imagine que tiene dos punteros a objetos y la asignación del primer objeto tiene éxito mientras que la segunda asignación falla, en ese caso termina con una pérdida de recursos. Esto se puede resolver usando punteros "inteligentes". Pero en ese caso se inicializarán en la lista de inicialización, y si no desea asignarles valor dos veces, es mejor que asignen memoria allí que en el cuerpo del constructor.

+0

No pensé en el posible problema de seguridad de excepción. –

1

No es necesario, especialmente si se inicializa inmediatamente el cuerpo constructor; sin embargo, si la inicialización no es tan obvia, ¿por qué no NULL para evitar el acceso accidental a la variable no inicializada?

  • mínima sobrecarga de rendimiento
  • ahorra mucho tiempo/solución de problemas con la depuración de la caza de errores loca
+0

* 'crazy bug hunting' * - definitivamente. Ahora mismo estoy buscando una fuga de recursos (un objeto de usuario de Windows), y la mayoría de los problemas hacen que 'nuevas' llamadas en la lista de inicializadores: no hay antes/después en el mundo" atómico "de las listas de inicializadores. – Wolf

Cuestiones relacionadas