2012-07-05 28 views
13

La función make_shared() de Boost promete ser a prueba de excepciones al intentar crear un shared_ptr.¿Por qué boost no tiene un make_scoped()?

¿Por qué no hay make_scoped() equivalente? ¿Hay una mejor práctica común?

Aquí está un ejemplo de código de la boost::scoped_ptr documentation que parece inseguro para mí:

boost::scoped_ptr<Shoe> x(new Shoe); 

Esta línea de código va a hacer estas tres cosas con el fin:

  • asignar memoria del montón por Shoe
  • Llamar al constructor para Shoe
  • llamar al constructor para boost::scoped_ptr<Shoe>

Si el constructor de Shoe lanza una excepción, se filtró memoria. (vea la respuesta de R. Martinho Fernandes) El scoped_ptr no manejará la desasignación porque aún no se ha construido.

¿Esto es un descuido? ¿O hay una solución que no he notado?

+0

Este ejemplo es seguro, pero uno que no es: 'f (boost :: scoped_ptr (nuevo Shoe), g());'. La práctica de codificación para resolver el problema: siempre nombre punteros inteligentes como variables o miembros, no los construya como subexpresiones temporales. – aschepler

Respuesta

13

Si el constructor falla, no se pierde memoria. Eso es parte de la semántica de new, no hay punteros inteligentes involucrados:

struct Foo { Foo() { throw 23; } }; 
new Foo(); // no memory leaked 

La seguridad excepción añadido de make_shared proviene de cuando se está inicializando dos shared_ptr s en una expresión y los dos inicializaciones no se secuencian, como es el caso en los argumentos de llamada de función:

struct Bar { 
    Bar(bool fail) { 
     if(fail) throw 17; 
    } 
} 
f(shared_ptr<Bar>(new Bar(true)), shared_ptr<Bar>(new Bar(false))); 

Puesto que no hay secuenciación entre las evaluaciones de new Bar(true), shared_ptr<Bar>(new Bar(true)), new Bar(false) y shared_ptr<Bar>(new Bar(false)), puede ocurrir lo siguiente:

  1. new Bar(false) se evalúa y se realiza correctamente: se asigna la memoria;
  2. new Bar(true) se evalúa y falla: no pierde la memoria resultante de esta evaluación;

No shared_ptr se construyó en este momento, por lo que la memoria asignada en el # 1 ahora se ha filtrado.

+0

¿Suena como que te entiendo? 'new' promete atrapar excepciones lanzadas desde el constructor, liberar la memoria y luego volver a lanzar? –

+3

@Drew: Sí, algo en ese sentido. Si está interesado, esto se describe en el estándar en §5.3.4 párrafo 18. –

+3

Para completar otro propósito de 'make_shared' (en la mayoría de las implementaciones) es que asigna memoria para el recuento de referencias al mismo tiempo que el objeto creado (asigna un bloque lo suficientemente grande para el objeto y el recuento) para que no incurra en la penalización de rendimiento si hay dos asignaciones de montón. El 'scoped_ptr' (o' unique_ptr' en C++ 11 no necesita un recuento de referencia y, por lo tanto, no obtiene nada de él en ese departamento. – John5342

1

Si se lanza el zapato, Shoe no está fabricado, por lo que no hay nada que scoped_ptr realmente pueda hacer. ¿No? scoped_ptr x está en la pila y se limpiará en la salida del alcance.

14

scoped_ptr anterior a la semántica del movimiento y no es copiable por diseño. Por lo tanto, make_scoped sería imposible de implementar porque para devolver un objeto desde una función, su tipo debe ser movible o se puede copiar.

Cuestiones relacionadas