std:: shared_ptr
|
Definido en el encabezado
<memory>
|
||
|
template
<
class
T
>
class
shared_ptr
;
|
(desde C++11) | |
std::shared_ptr
es un puntero inteligente que mantiene la propiedad compartida de un objeto a través de un puntero. Varios
shared_ptr
pueden poseer el mismo objeto. El objeto se destruye y su memoria se desasigna cuando ocurre cualquiera de los siguientes casos:
-
el último
shared_ptrrestante que posee el objeto es destruido; -
el último
shared_ptrrestante que posee el objeto es asignado a otro puntero mediante operator= o reset() .
El objeto es destruido usando
delete-expression
o un custom deleter que se suministra a
shared_ptr
durante la construcción.
Un
shared_ptr
puede compartir la propiedad de un objeto mientras almacena un puntero a otro objeto. Esta característica puede utilizarse para apuntar a objetos miembro mientras se posee el objeto al que pertenecen. El puntero almacenado es al que se accede mediante
get()
, los operadores de desreferencia y los operadores de comparación. El puntero gestionado es el que se pasa al destructor cuando el contador de uso llega a cero.
Un
shared_ptr
también puede no poseer objetos, en cuyo caso se denomina
vacío
(un
shared_ptr
vacío puede tener un puntero almacenado no nulo si se utilizó el constructor de aliasing para crearlo).
Todas las especializaciones de
shared_ptr
cumplen con los requisitos de
CopyConstructible
,
CopyAssignable
, y
LessThanComparable
y son
contextualmente convertibles
a
bool
.
Todas las funciones miembro (incluyendo el constructor de copia y la asignación de copia) pueden ser llamadas por múltiples hilos en diferentes
shared_ptr
objetos sin sincronización adicional incluso si estos objetos son copias y comparten la propiedad del mismo objeto. Si múltiples hilos de ejecución acceden al mismo
shared_ptr
objeto sin sincronización y cualquiera de esos accesos utiliza una función miembro no constante de
shared_ptr
entonces ocurrirá una condición de carrera; el
std::atomic<shared_ptr>
puede utilizarse para prevenir la condición de carrera.
Contenidos |
Tipos de miembros
| Tipo de miembro | Definición | ||||
element_type
|
|
||||
weak_type
(desde C++17)
|
std:: weak_ptr < T > |
Funciones miembro
construye un nuevo
shared_ptr
(función miembro pública) |
|
destruye el objeto poseído si no hay más
shared_ptr
s que apunten 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 referencian el mismo objeto gestionado
(función miembro pública) |
|
|
(until C++20)
|
comprueba si el objeto gestionado está administrado únicamente por el objeto
shared_ptr
actual
(función miembro pública) |
|
comprueba si el puntero almacenado no es nulo
(función miembro pública) |
|
|
proporciona ordenación basada en propietario de punteros compartidos
(función miembro pública) |
|
|
(C++26)
|
proporciona hashing basado en propietario de punteros compartidos
(función miembro pública) |
|
(C++26)
|
proporciona comparación de igualdad basada en propietario de punteros compartidos
(función miembro pública) |
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 mediante 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 está poseído
(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)
|
compara con otro
shared_ptr
o con
nullptr
(plantilla de función) |
|
envía 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 operaciones atómicas para
std::shared_ptr
(plantilla de función) |
Clases auxiliares
|
(C++20)
|
puntero compartido atómico
(especialización de plantilla de clase) |
|
(C++11)
|
soporte de hash para
std::shared_ptr
(especialización de plantilla de clase) |
Guías de deducción (desde C++17)
Notas
La propiedad de un objeto solo puede compartirse mediante funciones de construcción o asignación que reciban otro objeto
shared_ptr
. Si se construye un nuevo
shared_ptr
utilizando únicamente el puntero subyacente en bruto mantenido por otro
shared_ptr
, este nuevo
shared_ptr
asumirá que no hay otras instancias de
shared_ptr
que posean el objeto que posee. Esto resultará (a menos que ocurra una asignación posterior) en la aplicación repetida del eliminador al mismo objeto durante la destrucción.
std::shared_ptr
puede utilizarse con un
tipo incompleto
T
. Sin embargo, el constructor a partir de un puntero en crudo (
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 (nótese que
std::unique_ptr
puede construirse a partir de un puntero en crudo a un tipo incompleto).
El
T
en
std
::
shared_ptr
<
T
>
puede ser un tipo de función: en este caso gestiona un puntero a función, en lugar de un puntero a objeto. Esto se utiliza a veces para mantener una biblioteca dinámica o un complemento cargado mientras alguna de sus funciones esté referenciada:
void del(void(*)()) {} void fun() {} int main() { std::shared_ptr<void()> ee(fun, del); (*ee)(); }
Notas de implementación
En una implementación típica,
shared_ptr
contiene solo dos punteros:
- el puntero almacenado (el devuelto por get() );
- un puntero al control block .
El bloque de control es un objeto asignado dinámicamente que contiene:
- ya sea un puntero al objeto gestionado o el objeto gestionado mismo;
- el eliminador (con tipo borrado);
- el asignador (con tipo borrado);
-
el número de
shared_ptrs que poseen el objeto gestionado; -
el número de
weak_ptrs que hacen referencia al objeto gestionado.
Cuando
shared_ptr
se crea 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 única asignación. El objeto gestionado se construye in-situ en un miembro de datos del bloque de control. Cuando
shared_ptr
se crea mediante 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 mantenido directamente por el
shared_ptr
es el que devuelve
get()
, mientras que el puntero/objeto mantenido por el bloque de control es el que será eliminado cuando el número de propietarios compartidos llegue 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 de
std::weak_ptr
también llegue a cero.
En implementaciones existentes, el número de weak pointers se incrementa ( [1] , [2] ) si existe un shared pointer al mismo bloque de control.
Para satisfacer los requisitos de seguridad de hilos, los contadores de referencia normalmente 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 forma segura el bloque de control).
Ejemplo
#include <chrono> #include <iostream> #include <memory> #include <mutex> #include <thread> using namespace std::chrono_literals; struct Base { Base() { std::cout << "Base::Base()\n"; } // Nota: el destructor no virtual está bien aquí ~Base() { std::cout << "Base::~Base()\n"; } }; struct Derived : public Base { Derived() { std::cout << "Derived::Derived()\n"; } ~Derived() { std::cout << "Derived::~Derived()\n"; } }; void print(auto rem, std::shared_ptr<Base> const& sp) { std::cout << rem << "\n\tget() = " << sp.get() << ", use_count() = " << sp.use_count() << '\n'; } void thr(std::shared_ptr<Base> p) { std::this_thread::sleep_for(987ms); std::shared_ptr<Base> lp = p; // seguro para hilos, aunque el // shared use_count se incrementa { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); print("Local pointer in a thread:", lp); } } int main() { std::shared_ptr<Base> p = std::make_shared<Derived>(); print("Created a shared Derived (as a pointer to Base)", p); std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset(); // liberar propiedad desde main print("Shared ownership between 3 threads and released ownership from main:", p); t1.join(); t2.join(); t3.join(); std::cout << "All threads completed, the last one deleted Derived.\n"; }
Salida posible:
Base::Base() Derived::Derived() Created a shared Derived (as a pointer to Base) get() = 0x118ac30, use_count() = 1 Shared ownership between 3 threads and released ownership from main: get() = 0, use_count() = 0 Local pointer in a thread: get() = 0x118ac30, use_count() = 5 Local pointer in a thread: get() = 0x118ac30, use_count() = 4 Local pointer in a thread: get() = 0x118ac30, use_count() = 2 Derived::~Derived() Base::~Base() All threads completed, the last one deleted Derived.
Ejemplo
#include <iostream> #include <memory> struct MyObj { MyObj() { std::cout << "MyObj construido\n"; } ~MyObj() { std::cout << "MyObj destruido\n"; } }; struct Container : std::enable_shared_from_this<Container> // nota: herencia pública { std::shared_ptr<MyObj> memberObj; void CreateMember() { memberObj = std::make_shared<MyObj>(); } std::shared_ptr<MyObj> GetAsMyObj() { // Usar un alias shared ptr para miembro return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get()); } }; #define COUT(str) std::cout << '\n' << str << '\n' #define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n' int main() { COUT("Creando contenedor compartido"); std::shared_ptr<Container> cont = std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creando miembro"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creando otro contenedor compartido"); std::shared_ptr<Container> cont2 = cont; DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("GetAsMyObj"); std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Copiando alias obj"); std::shared_ptr<MyObj> myobj2 = myobj1; DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Reiniciando cont2"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Reiniciando myobj2"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Restableciendo cont"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); }
Salida:
Creando contenedor compartido cont.use_count() = 1 cont->memberObj.use_count() = 0 Creando miembro MyObj construido cont.use_count() = 1 cont->memberObj.use_count() = 1 Creando otro contenedor compartido cont.use_count() = 2 cont->memberObj.use_count() = 1 cont2.use_count() = 2 cont2->memberObj.use_count() = 1 GetAsMyObj myobj1.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 cont2.use_count() = 3 cont2->memberObj.use_count() = 1 Copiando objeto alias myobj1.use_count() = 4 myobj2.use_count() = 4 cont.use_count() = 4 cont->memberObj.use_count() = 1 cont2.use_count() = 4 cont2->memberObj.use_count() = 1 Reseteando cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 Reseteando myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 Reseteando cont myobj1.use_count() = 1 cont.use_count() = 0 MyObj destruido
Véase también
|
(C++11)
|
puntero inteligente con semántica de propiedad única de objetos
(plantilla de clase) |
|
(C++11)
|
referencia débil a un objeto gestionado por
std::shared_ptr
(plantilla de clase) |