std:: unique_ptr
|
Definido en el encabezado
<memory>
|
||
|
template
<
class
T,
|
(1) | (desde C++11) |
|
template
<
class
T,
|
(2) | (desde C++11) |
std::unique_ptr
es un puntero inteligente que posee (es responsable de) y gestiona otro objeto a través de un puntero, y posteriormente elimina ese objeto cuando el
unique_ptr
sale del ámbito.
El objeto se elimina, utilizando el eliminador asociado, cuando ocurre cualquiera de los siguientes casos:
-
el objeto
unique_ptradministrador es destruido. -
el objeto
unique_ptradministrador es asignado a otro puntero mediante operator= o reset() .
El objeto es eliminado, utilizando un eliminador potencialmente suministrado por el usuario, llamando a
get_deleter
(
)
(
ptr
)
. El eliminador por defecto (
std::default_delete
) utiliza el operador
delete
, que destruye el objeto y desasigna la memoria.
Un
unique_ptr
puede no poseer ningún objeto, en cuyo caso se describe como
vacío
.
Hay dos versiones de
unique_ptr
:
- Gestiona un único objeto (por ejemplo, asignado con new ).
- Gestiona un array de objetos asignado dinámicamente (por ejemplo, asignado con new [ ] ).
La clase satisface los requisitos de MoveConstructible y MoveAssignable , pero no los de CopyConstructible ni CopyAssignable .
Si
T*
no era un tipo válido (por ejemplo,
T
es un tipo de referencia), un programa que instancia la definición de
std
::
unique_ptr
<
T, Deleter
>
está mal formado.
| Requisitos de tipo | ||
-
Deleter
debe ser
FunctionObject
o referencia lvalue a un
FunctionObject
o referencia lvalue a función, invocable con un argumento de tipo
unique_ptr
<
T, Deleter
>
::
pointer
.
|
Contenidos |
Notas
Solo los
unique_ptr
no constantes pueden transferir la propiedad del objeto gestionado a otro
unique_ptr
. Si el tiempo de vida de un objeto está gestionado por un
const
std
::
unique_ptr
, está limitado al ámbito en el que se creó el puntero.
unique_ptr
se utiliza comúnmente para gestionar el ciclo de vida de objetos, incluyendo:
- proporcionar seguridad de excepciones a clases y funciones que manejan objetos con tiempo de vida dinámico, garantizando la eliminación tanto en la salida normal como en la salida mediante excepción.
- transferir la propiedad de objetos de propiedad única con tiempo de vida dinámico a funciones.
- adquiriendo propiedad de objetos de propiedad única con tiempo de vida dinámico desde funciones.
- como el tipo de elemento en contenedores conscientes del movimiento, tales como std::vector , que mantienen punteros a objetos asignados dinámicamente (por ejemplo, si se desea comportamiento polimórfico).
unique_ptr
puede ser construido para un
tipo incompleto
T
, como para facilitar su uso como manejador en el
idiom pImpl
. Si se utiliza el eliminador por defecto,
T
debe estar completo en el punto del código donde se invoca el eliminador, lo cual ocurre en el destructor, el operador de asignación de movimiento, y la función miembro
reset
de
unique_ptr
. (En contraste,
std::shared_ptr
no puede ser construido desde un puntero crudo a tipo incompleto, pero puede ser destruido donde
T
es incompleto). Nótese que si
T
es una especialización de plantilla de clase, el uso de
unique_ptr
como operando, ej.
!
p
requiere que los parámetros de
T
estén completos debido a
ADL
.
Si
T
es una
clase derivada
de alguna clase base
B
, entonces
unique_ptr
<
T
>
es
implícitamente convertible
a
unique_ptr
<
B
>
. El eliminador predeterminado del
unique_ptr
<
B
>
resultante usará
operator delete
para
B
, lo que conduce a
comportamiento indefinido
a menos que el destructor de
B
sea
virtual
. Nótese que
std::shared_ptr
se comporta de forma diferente:
std::
shared_ptr
<
B
>
usará el
operator delete
para el tipo
T
y el objeto poseído será eliminado correctamente incluso si el destructor de
B
no es
virtual
.
A diferencia de
std::shared_ptr
,
unique_ptr
puede gestionar un objeto a través de cualquier tipo de handle personalizado que satisfaga
NullablePointer
. Esto permite, por ejemplo, gestionar objetos ubicados en memoria compartida, proporcionando un
Deleter
que defina
typedef
boost::offset_ptr
pointer;
u otro
fancy pointer
.
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_lib_constexpr_memory
|
202202L
|
(C++23) |
constexpr
std::unique_ptr
|
Tipos anidados
| Tipo | Definición |
| pointer |
std::
remove_reference
<
Deleter
>
::
type
::
pointer
si ese tipo existe, de lo contrario
T*
. Debe satisfacer
NullablePointer
|
| element_type |
T
, el tipo del objeto gestionado por este
unique_ptr
|
| deleter_type |
Deleter
, el objeto función o referencia lvalue a función o a objeto función, a ser llamado desde el destructor
|
Funciones miembro
construye un nuevo
unique_ptr
(función miembro pública) |
|
|
destruye el objeto gestionado si existe
(función miembro pública) |
|
asigna el
unique_ptr
(función miembro pública) |
|
Modificadores |
|
|
devuelve un puntero al objeto gestionado y libera la propiedad
(función miembro pública) |
|
|
reemplaza el objeto gestionado
(función miembro pública) |
|
|
intercambia los objetos gestionados
(función miembro pública) |
|
Observadores |
|
|
devuelve un puntero al objeto gestionado
(función miembro pública) |
|
|
devuelve el deleter utilizado para la destrucción del objeto gestionado
(función miembro pública) |
|
|
verifica si existe un objeto gestionado asociado
(función miembro pública) |
|
Versión de objeto único,
|
|
|
desreferencia el puntero al objeto gestionado
(función miembro pública) |
|
Versión de array,
|
|
|
proporciona acceso indexado al array gestionado
(función miembro pública) |
|
Funciones no miembro
|
(C++14)
(C++20)
|
crea un unique pointer que gestiona un nuevo objeto
(plantilla de función) |
|
(eliminado en C++20)
(C++20)
|
compara con otro
unique_ptr
o con
nullptr
(plantilla de función) |
|
(C++20)
|
envía el valor del puntero gestionado a un flujo de salida
(plantilla de función) |
|
(C++11)
|
especializa el algoritmo
std::swap
(plantilla de función) |
Clases auxiliares
|
(C++11)
|
Soporte de hash para
std::unique_ptr
(especialización de plantilla de clase) |
Ejemplo
#include <cassert> #include <cstdio> #include <fstream> #include <iostream> #include <locale> #include <memory> #include <stdexcept> // clase auxiliar para la demostración de polimorfismo en tiempo de ejecución a continuación struct B { virtual ~B() = default; virtual void bar() { std::cout << "B::bar\n"; } }; struct D : B { D() { std::cout << "D::D\n"; } ~D() { std::cout << "D::~D\n"; } void bar() override { std::cout << "D::bar\n"; } }; // una función que consume un unique_ptr puede tomarlo por valor o por referencia de valor derecho std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } // función auxiliar para la demostración del eliminador personalizado a continuación void close_file(std::FILE* fp) { std::fclose(fp); } // unique_ptr-based linked list demo struct List { struct Node { int data; std::unique_ptr<Node> next; }; std::unique_ptr<Node> head; ~List() { // destruir nodos de lista secuencialmente en un bucle, el destructor predeterminado // habría invocado el destructor de su "next" recursivamente, lo que // causa desbordamiento de pila para listas suficientemente grandes. while (head) { auto next = std::move(head->next); head = std::mover(next); } } void push(int data) { head = std::unique_ptr<Node>(new Node{data, std::move(head)}); } }; int main() { std::cout << "1) Demostración de semántica de propiedad única\n"; { // Crear un recurso (de propiedad única) std::unique_ptr<D> p = std::make_unique<D>(); // Transfer ownership to “pass_through”, // que a su vez transfiere la propiedad de vuelta a través del valor de retorno std::unique_ptr<D> q = pass_through(std::move(p)); // “p” está ahora en un estado 'vacío' tras ser movido, igual a nullptr assert(!p); } std::cout << "\n" "2) Demostración de polimorfismo en tiempo de ejecución\n"; { // Crear un recurso derivado y apuntar a él mediante el tipo base std::unique_ptr<B> p = std::make_unique<D>(); // Dynamic dispatch funciona como se espera p->bar(); } std::cout << "\n" "3) Demostración de eliminador personalizado\n"; std::ofstream("demo.txt") << 'x'; // preparar el archivo para leer { using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>; unique_file_t fp(std::fopen("demo.txt", "r"), &close_file); if (fp) std::cout << char(std::fgetc(fp.get())) << '\n'; } // “close_file()” llamado aquí (si “fp” no es nulo) std::cout << "\n" "4) Demostración de eliminador de expresión lambda personalizado y seguridad de excepciones\n"; try { std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr) { std::cout << "destruyendo desde un deleter personalizado...\n"; delete ptr; }); throw std::runtime_error(""); // “p” tendría una fuga de memoria aquí si fuera un puntero simple } catch (const std::exception&) { std::cout << "Excepción capturada\n"; } std::cout << "\n" "5) Demostración de unique_ptr en forma de array\n"; { std::unique_ptr<D[]> p(new D[3]); } // “D::~D()” is called 3 times std::cout << "\n" "6) Demostración de lista enlazada\n"; { List wall; const int enough{1'000'000}; for (int beer = 0; beer != enough; ++beer) wall.push(beer); std::cout.imbue(std::locale("en_US.UTF-8")); std::cout << enough << " botellas de cerveza en la pared...\n"; } // destruye todas las cervezas }
Salida posible:
1) Demostración de semántica de propiedad única D::D D::bar D::~D 2) Demostración de polimorfismo en tiempo de ejecución D::D D::bar D::~D 3) Demostración de eliminador personalizado x 4) Demostración de eliminador con expresión lambda y seguridad ante excepciones D::D destruyendo desde un eliminador personalizado... D::~D Excepción capturada 5) Demostración de unique_ptr en forma de array D::D D::D D::D D::~D D::~D D::~D 6) Demostración de lista enlazada 1,000,000 botellas de cerveza en la pared...
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| LWG 4144 | C++11 |
T*
no se requería que formara un tipo válido
|
requerido |
Véase también
|
(C++11)
|
puntero inteligente con semántica de propiedad compartida de objetos
(plantilla de clase) |
|
(C++11)
|
referencia débil a un objeto gestionado por
std::shared_ptr
(plantilla de clase) |
|
(C++26)
|
un contenedor que contiene un objeto asignado dinámicamente con semántica de valor
(plantilla de clase) |
|
(C++17)
|
objetos que contienen instancias de cualquier tipo
CopyConstructible
(clase) |