2011-11-09 9 views
6

construcción de dos fases toma la siguiente forma:¿Los punteros inteligentes excluyen la necesidad de una construcción en dos fases?

struct something { 
    something() 
     : p1(NULL) 
     , p2(NULL) 
    { } 

    ~something() { 
     if (p1) delete p1; 
     if (p2) delete p2; 
    } 

    void initialize() { 
     p1 = new int(2); 
     p2 = new int(5); // May throw if allocation fails! 
    } 

    int* p1; 
    int* p2; 
}; 

el punto de que es que un constructor ingenuo (que no reloj por errores de asignación) será pérdida de memoria: destructor de un objeto construido parcialmente nunca es llamado .

Mi pregunta: ¿es seguro el siguiente código, y por corrolario, los punteros inteligentes obvian la construcción de dos fases?

struct something { 
    something() 
     : p1(new int(2)) 
     , p2(new int(5)) 
    { } 

    std::unique_ptr<int> p1; 
    std::unique_ptr<int> p2; 
}; 
+6

No es necesario un condicional antes de 'borrar'. En serio, no lo haces. Eliminar null está perfectamente bien. –

+0

No hay lanzamiento nuevo y eliminar también que funcionan con la asignación de memoria al igual que malloc()/free(). Pero incluso si la asignación en sí misma no falla utilizando new/delete que no arroje, los constructores de objetos aún pueden lanzar. –

+0

@KerrekSB tomado nota. :) –

Respuesta

5

Sí, su nuevo código está bien. Tenga en cuenta sin embargo, que hay una posible sutilmente en los casos más complejos:

#include <memory> 

struct foo { 
    foo(std::shared_ptr<int> a, std::shared_ptr<int> b) { } 
}; 

struct bar { 
    foo f; 
    bar() : f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)) { } 
}; 

int main() { 
    bar b; 
} 

no sería seguro sin embargo ya que el orden de evaluación de los argumentos del constructor de foo en la lista de inicialización de bar está especificado. Un compilador conforme puede elegir hacer un primer orden de evaluación de profundidad o de amplitud (o cualquier otra cosa siempre que todos sean evaluados correctamente al final). Esto significa que si el primer new int tuvo éxito, pero el segundo tira antes de que se construyan los objetos shared_ptr, la primera asignación que se realizará aún podría tener fugas.

Si desea hacer esto, hay dos soluciones posibles, además de simplemente volver a la construcción en dos fases: la primera podría ser un refactorizador, la segunda sería construir primero las shared_ptr individualmente como miembros de la barra , antes de f. Cuál de los dos es más apropiado es una decisión que creo que debe tomarse caso por caso.

5

Mi pregunta: ¿es el siguiente código de seguridad,

Sí, eso estaría bien.

struct something { 
    something() 
     : p(new int(5)) 
    { } 

    std::unique_ptr<int> p; 
}; 

Tenga en cuenta que el código ingenua

struct something { 
    something() 
     : p(new int(5)) 
    { } 

    int* p; 
}; 

estaría a salvo excepción, también, porque sólo hay una asignación que puede fallar. Creo que estás hablando más bien de

struct something { 
    something() 
     : p(new int(5)), q(new int) 
    { } 

    int *p, *q; 
}; 

que no. Los indicadores inteligentes también funcionarían en ese caso.

+0

Ay, tienes toda la razón; He actualizado el ejemplo para activar realmente la fuga. –

0

No necesita construcción en dos fases si simplemente maneja la excepción. Este es el camino de RIAA.

struct something { 
    something() 
     : p1(NULL) 
     , p2(NULL) 
    { 
     p1 = new int(2); 
     try { 
      p2 = new int(5); // May throw if allocation fails! 
     } catch (std::bad_alloc&) { 
      delete p1; //cleanup 
      throw; //rethrow 
     } 
    } 

    ~something() { 
     delete p1; 
     delete p2; 
    } 

    int* p1; 
    int* p2; 
}; 
+0

Si no estoy completamente equivocado, esto es explícitamente ** no ** el modo RAII, porque primero ** inicializa ** 'p1' con' NULL', y ** asigna más tarde ** a 'new int (2) '. – bitmask

+0

Esto hace que 'algo' cumpla RAII, aunque sus elementos internos no cumplen con RIAA.Cuando profundizas lo suficiente en el interior de cualquier clase de RIAA, eventualmente llegarás a una clase cuyos internos no cumplen con RIAA. Mi punto era la seguridad de la excepción y la falta de iniciación en dos fases. 'unique_ptr' tampoco tiene miembros de RIAA. –

+0

Ya veo. Mis disculpas. :) – bitmask

Cuestiones relacionadas