2012-07-27 13 views
11

¿Es posible escribir un puntero inteligente que asigne el objeto en sí mismo en su constructor, en lugar de que el desarrollador tenga que llamar al new? En otras palabras, en lugar de escribir:¿Por qué un puntero inteligente no puede llamar a new() para mí en su constructor?

std::unique_ptr<myClass> my_ptr(new myClass(arg1, arg2))

... uno podría escribir:

std::smarter_ptr<myClass> my_ptr(arg1, arg2)

Es la sintaxis del lenguaje capaz de expresar esto? ¿Sería esto deseable? ¿Horrible? Estoy pensando en particular en la protección contra este error (que me he hecho a mí mismo, por supuesto):

myFunction(std::unique_ptr<myClass>(new myClass()), std::unique_ptr<myClass>(new myClass()))

... que corre el riesgo fugas que sea objeto se asigna en primer lugar si la segunda asignación pasa y lanza antes de que el primer objeto esté instalado de forma segura en su puntero inteligente. Pero, ¿un puntero más inteligente realmente lo haría seguro?

+2

@Joe: Es una preocupación; es muy posible que el código generado ejecute las dos expresiones 'nuevas' antes de usar cualquiera de los resultados para inicializar un puntero inteligente; en cuyo caso obtendrá una fuga si la segunda arroja. –

+2

@Joe: Estás equivocado. 'tmp1 = new myClass(); tmp2 = new myClass(); arg1 = std :: unique_ptr (tmp1); arg2 = std :: unique_ptr (tmp2); myFunction (arg1, arg2); 'es un orden de ejecución perfectamente legal. –

+1

Bastante, estaba sondeando para probar que soy incorrecto. Gracias. – Joe

Respuesta

2

Esto es nuevamente posible con C++ 11 que agregó perfect forwarding y plantillas variadic.

2

Es esencialmente el mismo problema que necesita std::find y std::find_if. No puede distinguir este cursor de los ctors existentes de shared_ptr en el caso new myClass(arg1). La cantidad de argumentos es igual, y arg1 puede tener cualquier tipo.

Por lo tanto, se necesita otro nombre, y eso es make_shared

+0

Esto se puede solucionar fácilmente con tipos de etiquetas, como 'shared_ptr (std :: inplace_args, args ...)', que es también lo que hace el estándar para pasar argumentos del asignador a ciertas funciones variadic. – Xeo

+0

@Xeo, cierto, por lo que 'make_shared' no es esencial, podría haberse hecho como sugiere OP, pero' make_shared (args ...) 'es menos tipeo de todos modos :) –

+0

@Xeo: Indeed. Hay muchas soluciones posibles, y C++ no es completamente consistente. – MSalters

11

En general, esto no se puede hacer con el constructor de un puntero inteligente; habría una ambigüedad sobre si un argumento de puntero debería usarse para inicializar el puntero inteligente, o reenviarse para crear un nuevo objeto.

Se puede hacer con una función de fábrica, por ejemplo:

template <typename T, typename... Args> 
std::unique_ptr<T> make_unique(Args&&... args) { 
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); 
} 

Si está utilizando std::shared_ptr, entonces se puede utilizar std::make_shared. Esto también ofrece la ventaja de que solo requiere una asignación de memoria, donde std::shared_ptr<T>(new T) requerirá una para el objeto y una segunda para el recuento de referencia compartida.

+0

Seguramente la ambigüedad solo existiría si la función se añadiera a los punteros inteligentes existentes, ¿no? Todavía tengo curiosidad sobre por qué no se implementaron como 'make_shared()' para empezar; hay una asimetría que me molesta al hacer que el puntero inteligente llame 'delete' pero tener que llamar' new' uno mismo. – bythescruff

Cuestiones relacionadas