std::shared_ptr<T>::shared_ptr
constexpr shared_ptr() noexcept; |
(1) | |
constexpr shared_ptr( std::nullptr_t ) noexcept; |
(2) | |
template< class Y > explicit shared_ptr( Y* ptr ); |
(3) | |
template< class Y, class Deleter > shared_ptr( Y* ptr, Deleter d ); |
(4) | |
template< class Deleter > shared_ptr( std::nullptr_t ptr, Deleter d ); |
(5) | |
template< class Y, class Deleter, class Alloc > shared_ptr( Y* ptr, Deleter d, Alloc alloc ); |
(6) | |
template< class Deleter, class Alloc > shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc ); |
(7) | |
template< class Y > shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept; |
(8) | |
template< class Y > shared_ptr( shared_ptr<Y>&& r, element_type* ptr ) noexcept; |
(8) | (desde C++20) |
shared_ptr( const shared_ptr& r ) noexcept; |
(9) | |
template< class Y > shared_ptr( const shared_ptr<Y>& r ) noexcept; |
(9) | |
shared_ptr( shared_ptr&& r ) noexcept; |
(10) | |
template< class Y > shared_ptr( shared_ptr<Y>&& r ) noexcept; |
(10) | |
template< class Y > explicit shared_ptr( const std::weak_ptr<Y>& r ); |
(11) | |
template< class Y > shared_ptr( std::auto_ptr<Y>&& r ); |
(12) | (eliminado en C++17) |
template< class Y, class Deleter > shared_ptr( std::unique_ptr<Y, Deleter>&& r ); |
(13) | |
Construye un nuevo shared_ptr a partir de una variedad de tipos de punteros que se refieren a un objeto a gestionar.
|
A los efectos de la siguiente descripción, se dice que un puntero de tipo |
(desde C++17) |
shared_ptr sin un objeto gestionado. Es decir, un shared_ptr vacío.shared_ptr con ptr como el puntero al objeto gestionado.
|
Para (3-4,6), |
(hasta C++17) |
|
Si |
(desde C++17) |
delete delete ptr si T no es un tipo array; delete[] ptr si T es un tipo array (desde C++17) como el eliminador. Y debe ser un tipo completo. La expresión delete debe estar bien formada, tener un comportamiento bien definido y no lanzar ninguna excepción. Este constructor tampoco participa en la resolución de sobrecarga si la expresión delete no está bien formada. (desde C++17)d como el eliminador. La expresión d(ptr) debe estar bien formada, tener un comportamiento bien definido y no lanzar ninguna excepción. La construcción de d y del eliminador almacenado de d no deben lanzar excepciones.
|
|
(hasta C++17) |
|
Además, estos constructores no participan en la resolución de sobrecarga si la expresión |
(desde C++17) |
alloc para la asignación de datos para uso interno. Alloc debe ser un Allocator.shared_ptr que comparte información de posesión con el valor inicial de r, pero mantiene un puntero no relacionado y no gestionado ptr. Si este shared_ptr es el último del grupo en salir de ámbito, llamará al eliminador almacenado para el objeto gestionado originalmente por r. Sin embargo, llamar a get() en este shared_ptr siempre devolverá una copia de ptr. Es responsabilidad del programador asegurarse de que este ptr siga siendo válido mientras exista este shared_ptr, como en los casos de uso típicos donde ptr es un miembro del objeto administrado por r o es un alias (p. ej., downcast) de r.get() Para la segunda sobrecarga que toma un r-valor, r está vacío y r.get() == nullptr después de la llamada. (desde C++20)shared_ptr que comparte posesión del objeto gestionado por r. Si r no gestiona ningún objeto, *this tampoco gestiona ningún objeto. La sobrecarga de plantilla no participa en la resolución de sobrecarga si Y* no es implícitamente convertible a (hasta C++17)compatible con (desde C++17) T*.shared_ptr a partir de r. Después de la construcción, *this contiene una copia del estado anterior de r, r está vacío y su puntero almacenado es nulo. La sobrecarga de plantilla no participa en la resolución de sobrecarga si Y* no es implícitamente convertible a (hasta C++17)compatible con (desde C++17) T*.shared_ptr que comparte posesión del objeto gestionado por r. Y* debe ser implícitamente convertible a T*. (hasta C++17)Esta sobrecarga solo participa en la resolución de sobrecargas si Y* es compatible con T*. (desde C++17) Observa que r.lock() puede usarse para el mismo propósito: la diferencia es que este constructor lanza una excepción si el argumento está vacío, mientras que std::weak_ptr<T>::lock() construye un std::shared_ptr vacío en ese caso.shared_ptr que almacena y posee el objeto que anteriormente era posesión de r. Y* debe ser convertible a T*. Después de la construcción, r está vacío.shared_ptr que gestiona el objeto actualmente gestionado por r. El eliminador asociado con r se almacena para la eliminación futura del objeto gestionado. r no gestiona ningún objeto después de la llamada. Esta sobrecarga no participa en la resolución de sobrecarga si std::unique_ptr<Y, Deleter>::pointer no es compatible con T*.
Si r.get() es un puntero nulo, esta sobrecarga es equivalente al constructor por defecto (1). |
(desde C++17) |
Deleter es un tipo referencia, es equivalente a shared_ptr(r.release(), std::ref(r.get_deleter()). De lo contrario, es equivalente a shared_ptr(r.release(), std::move(r.get_deleter())) Cuando T no es un tipo array, las sobrecargas (3), (4) y (6) habilitan shared_from_this con ptr, y la sobrecarga (13) habilita shared_from_this con el puntero devuelto por r.release().
Parámetros
| ptr | - | Un puntero al objeto a gestionar. |
| d | - | Un eliminador a usar para destruir el objeto. |
| alloc | - | Un asignador de memoria a usar para las asignaciones de datos para uso interno. |
| r | - | Otro puntero inteligente con el que compartir la posesión o del que adquirir la posesión. |
Excepciones
delete ptr si T no es un tipo array, y llama a delete[] ptr de lo contrario (desde C++17).d(ptr) si ocurre una excepción.r.expired() == true. El constructor no tiene efecto en este caso.Notas
Cuando se dice que un constructor habilita shared_from_this con un puntero ptr de tipo U*, significa que determina si U tiene una clase base no ambigua y accesible (desde C++17) que es una especialización de std::enable_shared_from_this, y si es así, el constructor evalúa la declaración:
if (ptr != nullptr && ptr->weak_this.expired())
ptr->weak_this = std::shared_ptr<std::remove_cv_t<U>>(*this,
const_cast<std::remove_cv_t<U>*>(ptr));
Donde weak_this es el miembro std::weak_ptr mutable oculto de std::enable_shared_from_this. La asignación al miembro weak_this no es atómica y entra en conflicto con cualquier acceso potencialmente concurrente al mismo objeto. Esto garantiza que las llamadas futuras a shared_from_this() compartirán la posesión con el std::shared_ptr creado por este constructor de puntero sin formato.
La prueba ptr->weak_this.expired() en el código de exposición anterior asegura que weak_this no se reasigna si ya indica un propietario. Esta prueba es necesaria a partir de C++17.
Las sobrecargas de puntero sin formato asumen la posesión del objeto apuntado. Por lo tanto, construir un shared_ptr utilizando la sobrecarga de puntero sin formato para un objeto que ya está gestionado por un shared_ptr, como por shared_ptr(ptr.get()) es probable que conduzca a un comportamiento no definido, incluso si el objeto es de un tipo derivado de std::enable_shared_from_this.
Debido a que el constructor por defecto es constexpr, los shared_ptr estáticos se inicializan como parte de la inicialización estática no local, antes de que comience cualquier inicialización dinámica no local. Esto hace que sea seguro usar shared_ptr en un constructor de cualquier objeto estático.
En C++11 y C++14 es válido construir un std::shared_ptr<T> a partir de un std::unique_ptr<T[]>:
std::unique_ptr<int[]> arr(new int[1]);
std::shared_ptr<int> ptr(std::move(arr));
Dado que shared_ptr obtiene su eliminador (un objeto std::default_delete<T[]>) de std::unique_ptr, el array se desasignará correctamente.
Esto ya no está permitido en C++17. En su lugar, se debe usar la forma de array std::shared_ptr<T[]>.
Ejemplo
#include <memory>
#include <iostream>
struct Foo {
int id{0};
Foo(int i = 0) : id{i} { std::cout << "Foo::Foo(" << i << ")\n"; }
~Foo() { std::cout << "Foo::~Foo(), id=" << id << '\n'; }
};
struct D {
void operator()(Foo* p) const {
std::cout << "Llamar a delete desde el objeto función. Foo::id=" << p->id << '\n';
delete p;
}
};
int main()
{
{
std::cout << "1) constructor sin un objeto gestionado\n";
std::shared_ptr<Foo> sh1;
}
{
std::cout << "2) constructor con objeto\n";
std::shared_ptr<Foo> sh2(new Foo{10});
std::cout << "sh2.use_count(): " << sh2.use_count() << '\n';
std::shared_ptr<Foo> sh3(sh2);
std::cout << "sh2.use_count(): " << sh2.use_count() << '\n';
std::cout << "sh3.use_count(): " << sh3.use_count() << '\n';
}
{
std::cout << "3) constructor con objeto y eliminador\n";
std::shared_ptr<Foo> sh4(new Foo{11}, D());
std::shared_ptr<Foo> sh5(new Foo{12}, [](auto p) {
std::cout << "Llamar a delete desde una lambda... p->id=" << p->id << "\n";
delete p;
});
}
}
Salida:
1) constructor sin un objeto gestionado
2) constructor con objeto
Foo::Foo(10)
sh2.use_count(): 1
sh2.use_count(): 2
sh3.use_count(): 2
Foo::~Foo(), id=10
3) constructor con objeto y eliminador
Foo::Foo(11)
Foo::Foo(12)
Llamar a delete desde una lambda... p->id=12
Foo::~Foo(), id=12
Llamar a delete desde el objeto función. Foo::id=11
Foo::~Foo(), id=11
Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
| ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
|---|---|---|---|
| LWG 3548 | C++11 | El constructor de unique_ptr construía el eliminador por copia
|
En su lugar lo construye por movimiento |
Véase también
| 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) | |
(C++11) |
Permite a un objeto crear un puntero compartido (shared_ptr) refiriéndose a sí mismo. (plantilla de clase) |