¿Cómo declarar std :: unique_ptr y para qué sirve?

95

Intento entender cómo std::unique_ptrfunciona y para eso encontré este documento. El autor parte del siguiente ejemplo:

#include <utility>  //declarations of unique_ptr
using std::unique_ptr;
// default construction
unique_ptr<int> up; //creates an empty object
// initialize with an argument
unique_ptr<int> uptr (new int(3));
double *pd= new double;
unique_ptr<double> uptr2 (pd);
// overloaded * and ->
*uptr2 = 23.5;
unique_ptr<std::string> ups (new std::string("hello"));
int len=ups->size();

Lo que me confunde es que en esta línea

unique_ptr<int> uptr (new int(3));

Usamos integer como argumento (entre paréntesis) y aquí

unique_ptr<double> uptr2 (pd);

utilizamos un puntero como argumento. ¿Hace alguna diferencia?

Lo que tampoco me queda claro es cómo los punteros, declarados de esta manera, serán diferentes de los punteros declarados de una manera "normal".

romano
fuente
13
new int(3)devuelve un puntero al nuevo int, como si pdfuera un puntero al nuevo double.
David Schwartz

Respuestas:

89

El constructor de unique_ptr<T>acepta un puntero sin formato a un objeto de tipo T(por lo tanto, acepta a T*).

En el primer ejemplo:

unique_ptr<int> uptr (new int(3));

El puntero es el resultado de una newexpresión, mientras que en el segundo ejemplo:

unique_ptr<double> uptr2 (pd);

El puntero se almacena en la pdvariable.

Conceptualmente, nada cambia (está construyendo un a unique_ptrpartir de un puntero sin formato), pero el segundo enfoque es potencialmente más peligroso, ya que le permitiría, por ejemplo, hacer:

unique_ptr<double> uptr2 (pd);
// ...
unique_ptr<double> uptr3 (pd);

Por lo tanto, tener dos punteros únicos que encapsulan efectivamente el mismo objeto (violando así la semántica de un puntero único ).

Por eso, la primera forma para crear un puntero único es mejor, cuando es posible. Tenga en cuenta que en C ++ 14 podremos hacer:

unique_ptr<int> p = make_unique<int>(42);

Lo que es más claro y seguro. Ahora con respecto a esta duda tuya:

Lo que tampoco me queda claro es cómo los punteros, declarados de esta manera, serán diferentes de los punteros declarados de una manera "normal".

Se supone que los punteros inteligentes modelan la propiedad del objeto y se encargan automáticamente de destruir el objeto puntiagudo cuando el último puntero (inteligente, propietario) a ese objeto queda fuera de alcance.

De esta manera, no tiene que recordar haber hecho deleteen objetos asignados dinámicamente (el destructor del puntero inteligente lo hará por usted) ni preocuparse por si no eliminará la referencia de un puntero (colgante) a un objeto que ya ha sido destruido:

{
    unique_ptr<int> p = make_unique<int>(42);
    // Going out of scope...
}
// I did not leak my integer here! The destructor of unique_ptr called delete

Ahora unique_ptres un puntero inteligente que modela la propiedad única, lo que significa que en cualquier momento de su programa habrá solo un puntero (propietario) al objeto puntiagudo, por eso unique_ptrno se puede copiar.

Siempre que use punteros inteligentes de una manera que no rompa el contrato implícito que requieren que cumpla, tendrá la garantía de que no se filtrará memoria y se aplicará la política de propiedad adecuada para su objeto. Los punteros crudos no le dan esta garantía.

Andy Prowl
fuente
3
Hola, no pude entender nada sobre model object ownership, integer leaken el código o enforcing ownership policy for object. ¿Podría sugerir temas / recursos para aprender estos conceptos?
Llama de udun
1
No puedo usar unique_ptr, sin obtener un error:, The text ">" is unexpected. It may be that this token was intended as a template argument list terminator but the name is not known to be a template.aunque tengo #include <utility>y #include <memory>. ¿Algún consejo?
Anónimo
15

No hay diferencia en trabajar en ambos conceptos de asignación a unique_ptr.

int* intPtr = new int(3);
unique_ptr<int> uptr (intPtr);

es parecido a

unique_ptr<int> uptr (new int(3));

Aquí unique_ptr elimina automáticamente el espacio ocupado por uptr.


cómo los punteros, declarados de esta manera, serán diferentes de los punteros declarados de una manera "normal".

Si crea un número entero en el espacio del montón (usando nueva palabra clave o malloc ), tendrá que borrar esa memoria por su cuenta (usando delete o free respectivamente).

En el siguiente código,

int* heapInt = new int(5);//initialize int in heap memory
.
.//use heapInt
.
delete heapInt;

Aquí, tendrá que eliminar heapInt, cuando termine de usar. Si no se elimina, se produce una pérdida de memoria.

Para evitar tales fugas de memoria, se utiliza unique_ptr , donde unique_ptr elimina automáticamente el espacio ocupado por heapInt cuando sale del alcance. Por lo tanto, no es necesario eliminar o liberar para unique_ptr.

fury.slay
fuente
10

Se garantiza que los punteros únicos destruirán el objeto que administran cuando se salgan de su alcance. http://en.cppreference.com/w/cpp/memory/unique_ptr

En este caso:

unique_ptr<double> uptr2 (pd);

pdserá destruido cuando uptr2salga de su alcance. Esto facilita la gestión de la memoria mediante la eliminación automática.

El caso de unique_ptr<int> uptr (new int(3));no es diferente, excepto que el puntero sin formato no se asigna a ninguna variable aquí.

fatihk
fuente
-1

Desde cppreference , uno de los std::unique_ptrconstructores es

explícito unique_ptr (puntero p) noexcept;

Entonces, crear un nuevo std::unique_ptres pasar un puntero a su constructor.

unique_ptr<int> uptr (new int(3));

O es lo mismo que

int *int_ptr = new int(3);
std::unique_ptr<int> uptr (int_ptr);

La diferencia es que no tienes que limpiar después de usarlo. Si no usa std::unique_ptr(puntero inteligente), tendrá que eliminarlo así

delete int_ptr;

cuando ya no lo necesite o provocará una pérdida de memoria.

Tevada
fuente