std::shared_ptr
Definido en el archivo de encabezado <memory>
|
||
template< class T > class shared_ptr; |
(desde C++11) | |
std::shared_ptr
es un puntero inteligente que retiene la posesión compartida de un objeto a través de un puntero. Varios objetos shared_ptr
pueden poseer el mismo objeto. El objeto se destruye y su memoria se desasigna cuando sucede uno de lo siguientes:
- el último
shared_ptr
restante que posee el objeto se destruye; - al último
shared_ptr
restante que posee el objeto se le asigna otro puntero mediante operator= o reset().
El objeto se destruye usando la expresión-delete o un eliminador personalizado que se suplementa al shared_ptr
durante la construcción.
Un shared_ptr
puede compartir la posesión de un objeto mientras almacena un puntero a otro objeto. Esta función se puede utilizar para apuntar a objetos miembro mientras se posee el objeto al que pertenecen. El puntero almacenado es al que se accede por get(), los operadores de desreferencia y comparación. El puntero gestionado es el que se pasa al eliminador cuando el recuento de uso llega a cero.
Un shared_ptr
también puede no poseer objetos, en cuyo caso se le llama vacío (un shared_ptr
vacío puede almacenar un puntero no-nulo si el constructor de alias se utilizó para crearlo).
Todas las especializacines de shared_ptr
cumplen con los requerimientos de CopyConstructible, CopyAssignable, y LessThanComparable y son contextualmente convertibles a bool
.
Todas las funciones miembro (incluido el constructor de copia y la asignación de copia) pueden invocarse por varios hilos en diferentes instancias de shared_ptr
sin sincronización adicional, incluso si estas instancias son copias y comparten la posesión del mismo objeto. Si varios hilos de ejecución acceden a la misma instancia de shared_ptr
sin sincronización y cualquiera de esos accesos utiliza una función miembro no constante de shared_ptr
, se producirá una carrera de datos; las sobrecargas shared_ptr
de funciones atómicas se pueden utilizar para evitar la carrera de datos.
Contenido |
[editar] Tipos miembro
Tipo miembro | Definición | ||||
element_type |
| ||||
weak_type (desde C++17) | std::weak_ptr<T> |
[editar] Funciones miembro
Construye un nuevo shared_ptr . (función miembro pública) | |
Destruye el objeto poseído si ningún shared_ptr está enlazado a él. (función miembro pública) | |
Asigna el shared_ptr . (función miembro pública) | |
Modificadores | |
Reemplaza el objeto gestionado. (función miembro pública) | |
Intercambia los objetos gestionados. (función miembro pública) | |
Observadores | |
Devuelve el puntero almacenado. (función miembro pública) | |
Desreferencia el puntero almacenado. (función miembro pública) | |
(C++17) |
Proporciona acceso indexado al array almacenado. (función miembro pública) |
devuelve el número de objetos shared_ptr que se refieren al mismo objeto gestionado. (función miembro pública) | |
(hasta C++20) |
Comprueba si el objeto gestionado se gestiona solo por la instancia de shared_ptr actual. (función miembro pública) |
Comprueba si el puntero almacenado no es nulo. (función miembro pública) | |
Proporciona un ordenamiento de punteros compartidos basado en propietario. (función miembro pública) |
[editar] Funciones no miembro
Crea un puntero compartido que gestiona un nuevo objeto. (plantilla de función) | |
Crea un puntero compartido que gestiona un nuevo objeto asignado usando un asignador. (plantilla de función) | |
Aplica static_cast, dynamic_cast, const_cast, o reinterpret_cast al puntero almacenado. (plantilla de función) | |
Devuelve el eliminador del tipo especificado, si se posee. (plantilla de función) | |
(eliminado en C++20)(eliminado en C++20)(eliminado en C++20)(eliminado en C++20)(eliminado en C++20)(C++20) |
Se compara con otro
|
Emite el valor del puntero almacenado a un flujo de salida. (plantilla de función) | |
(C++11) |
Especializa el algoritmo std::swap. (plantilla de función) |
Especializa las operaciones atómicas para std::shared_ptr. (plantilla de función) |
[editar] Clases auxiliares
(C++20) |
Puntero compartido atómico. (especialización de plantilla de clase) |
(C++11) |
Apoyo de generación de dispersión para std::shared_ptr. (especialización de plantilla de clase) |
[editar] Guías de deducción(desde C++17)
[editar] Notas
La posesión de un objeto solo se puede compartir con otro shared_ptr
mediante la construcción por copia o asignando por copia su valor a otro shared_ptr
. La construcción de un nuevo shared_ptr
usando el puntero sin formato subyacente poseído por otro shared_ptr
conduce a un comportamiento no definido.
Un std::shared_ptr
puede usarse con un tipo incompleto T
. Sin embargo, el constructor a partir de un puntero sin formato (template<class Y> shared_ptr(Y*)) y la función miembro template<class Y> void reset(Y*) solo pueden llamarse con un puntero a un tipo completo (ten en cuenta que std::unique_ptr puede construirse a partir de un puntero sin formato a un tipo incompleto).
El tipo T
en std::shared_ptr<T>
puede ser un tipo función: en este caso, gestiona un puntero a función, en lugar de un puntero a objeto. Esto a veces se usa para mantener una biblioteca dinámica o un complemento (plug-in) cargado siempre que se haga referencia a cualquiera de sus funciones:
void del(void(*)()) {} void fun() {} int main(){ std::shared_ptr<void()> ee(fun, del); (*ee)(); }
[editar] Notas de la implementación
En una implementación típica, shared_ptr
mantiene solo dos punteros:
- el puntero almacenado (el devuelto por get());
- un puntero a un bloque de control.
El bloque de control es un objeto asignado dinámicamente en memoria que mantiene:
- o bien un puntero al objeto gestionado o el objeto gestionado mismo;
- el eliminador (borrado-de-tipo);
- el asignador (borrado-de-tipo);
- el número de
shared_ptr
s que poseen el objeto gestionado; - el número de
weak_ptr
s que se refieren al objeto gestionado.
Cuando se crea un shared_ptr
llamando a std::make_shared o std::allocate_shared, la memoria tanto para el bloque de control como para el objeto gestionado se crea con una sola asignación. El objeto gestionado se construye in situ en un dato miembro del bloque de control. Cuando se crea shared_ptr
a través de uno de los constructores de shared_ptr
, el objeto gestionado y el bloque de control deben asignarse por separado. En este caso, el bloque de control almacena un puntero al objeto gestionado.
El puntero que tiene directamente el shared_ptr
es el que devuelve get(), mientras que el puntero u objeto que tiene el bloque de control es el que se eliminará cuando el número de los propietarios compartidos llega a cero. Estos punteros no son necesariamente iguales.
El destructor de shared_ptr
decrementa el número de propietarios compartidos del bloque de control. Si ese contador llega a cero, el bloque de control llama al destructor del objeto gestionado. El bloque de control no se desasigna a sí mismo hasta que el contador del std::weak_ptr también llegue a cero.
En las implementaciones existentes, el número de punteros débiles se incrementa ([1], [2]) si existe un puntero compartido al mismo bloque de control.
Para satisfacer los requerimientos de seguridad frente a hilos, los contadores de referencias típicamente se incrementan usando un equivalente de std::atomic::fetch_add con std::memory_order_relaxed (el decremento requiere un ordenamiento más fuerte para destruir de manera segura el bloque de control).
[editar] Ejemplo
#include <iostream> #include <memory> #include <thread> #include <chrono> #include <mutex> struct Base { Base() { std::cout << " Base::Base()\n"; } // Nota: aquí el destructor no virtual está bien ~Base() { std::cout << " Base::~Base()\n"; } }; struct Derivada: public Base { Derivada() { std::cout << " Derivada::Derivada()\n"; } ~Derivada() { std::cout << " Derivada::~Derivada()\n"; } }; void thr(std::shared_ptr<Base> p) { std::this_thread::sleep_for(std::chrono::seconds(1)); std::shared_ptr<Base> lp = p; // seguro frente a hilos, incluso cuando la // cuenta de uso, use_count, se incrementa { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); std::cout << "puntero local en un hilo:\n" << " lp.get() = " << lp.get() << ", lp.use_count() = " << lp.use_count() << '\n'; } } int main() { std::shared_ptr<Base> p = std::make_shared<Derivada>(); std::cout << "Se creó Derivada compartida (como un puntero a Base)\n" << " p.get() = " << p.get() << ", p.use_count() = " << p.use_count() << '\n'; std::thread t1(thr, p), t2(thr, p), t3(thr, p); p.reset(); // liberar posesión desde main std::cout << "Propiedad compartida entre tres hilos y liberada\n" << "posesión desde main:\n" << " p.get() = " << p.get() << ", p.use_count() = " << p.use_count() << '\n'; t1.join(); t2.join(); t3.join(); std::cout << "Todos los hilos se completaron, el último liberó a Derivada\n"; }
Posible salida:
Base::Base() Derivada::Derivada() Se creó Derivada compartida (como un puntero a Base) p.get() = 0x2299b30, p.use_count() = 1 Propiedad compartida entre tres hilos y liberada posesión desde main: p.get() = 0, p.use_count() = 0 puntero local en un hilo: lp.get() = 0x2299b30, lp.use_count() = 5 puntero local en un hilo: lp.get() = 0x2299b30, lp.use_count() = 3 puntero local en un hilo: lp.get() = 0x2299b30, lp.use_count() = 2 Derivada::~Derivada() Base::~Base() Todos los hilos se completaron, el último liberó a Derivada
[editar] Véase también
(C++11) |
Puntero inteligente con semántica de posesión de objeto única. (plantilla de clase) |
(C++11) |
Referencia débil a un objeto gestionado por std::shared_ptr. (plantilla de clase) |