Namespaces
Variants

std:: make_shared, std:: make_shared_for_overwrite

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
Definido en el encabezado <memory>
template < class T, class ... Args >
shared_ptr < T > make_shared ( Args && ... args ) ;
(1) (desde C++11)
template < class T >
shared_ptr < T > make_shared ( std:: size_t N ) ;
(2) (desde C++20)
template < class T >
shared_ptr < T > make_shared ( ) ;
(3) (desde C++20)
template < class T >
shared_ptr < T > make_shared ( std:: size_t N, const std:: remove_extent_t < T > & u ) ;
(4) (desde C++20)
template < class T >
shared_ptr < T > make_shared ( const std:: remove_extent_t < T > & u ) ;
(5) (desde C++20)
template < class T >
shared_ptr < T > make_shared_for_overwrite ( ) ;
(6) (desde C++20)
template < class T >
shared_ptr < T > make_shared_for_overwrite ( std:: size_t N ) ;
(7) (desde C++20)

Asigna memoria para un objeto e inicializa el objeto con los argumentos proporcionados. Retorna un std::shared_ptr que gestiona el objeto recién creado.

1) El objeto es de tipo T , y se construye como si mediante :: new ( pv ) T ( std:: forward < Args > ( args ) ... ) , donde pv es un puntero void * a almacenamiento adecuado para contener un objeto de tipo T . Si el objeto debe ser destruido, se destruye como si mediante pt - > ~T ( ) , donde pt es un puntero a ese objeto de tipo T .

Esta sobrecarga participa en la resolución de sobrecarga solo si T no es un tipo array.

(since C++20)
2) El objeto es de tipo std:: remove_extent_t < T > [ N ] . Cada elemento tiene un valor inicial por defecto.
Esta sobrecarga participa en la resolución de sobrecarga solo si T es un tipo de array sin límite.
3) El objeto es de tipo T . Cada elemento tiene un valor inicial predeterminado.
Esta sobrecarga participa en la resolución de sobrecarga solo si T es un tipo de array con límites definidos.
4) El objeto es de tipo std:: remove_extent_t < T > [ N ] . Cada elemento tiene el valor inicial u .
Esta sobrecarga participa en la resolución de sobrecarga solo si T es un tipo de array sin límite.
5) El objeto es de tipo T . Cada elemento tiene el valor inicial u .
Esta sobrecarga participa en la resolución de sobrecarga solo si T es un tipo de array acotado.
6) El objeto es de tipo T .
  • Si T no es un tipo de arreglo, el objeto se construye como si fuera mediante :: new ( pv ) T , donde pv es un puntero void * a almacenamiento adecuado para contener un objeto de tipo T . Si el objeto debe ser destruido, se destruye como si fuera mediante pt - > ~T ( ) , donde pt es un puntero a ese objeto de tipo T .
  • Si T es un tipo de arreglo de tamaño fijo, el valor inicial no está especificado para cada elemento.
Esta sobrecarga participa en la resolución de sobrecarga solo si T no es un tipo de array o es un tipo de array con límites definidos.
7) El objeto es de tipo std:: remove_extent_t < T > [ N ] . El valor inicial no está especificado para cada elemento.
Esta sobrecarga participa en la resolución de sobrecarga solo si T es un tipo de array sin límite.

Contenidos

Inicialización y destrucción de elementos de array

Los elementos del array de tipo U se inicializan en orden ascendente de sus direcciones.

  • Si U no es un tipo array, cada elemento se construye como si fuera mediante la siguiente expresión, donde pv es un puntero void * a almacenamiento adecuado para contener un objeto de tipo U :
2,3) :: new ( pv ) U ( )
4,5) :: new ( pv ) U ( u )
6,7) :: new ( pv ) U
  • De lo contrario, inicializa recursivamente los elementos de cada elemento. Para la siguiente dimensión:
  • U se convierte en std:: remove_extent_t < U > .
  • Para las sobrecargas (4,5) , u se convierte en el elemento correspondiente de u .

Cuando finaliza el tiempo de vida del objeto gestionado por el std::shared_ptr retornado, o cuando la inicialización de un elemento del array lanza una excepción, los elementos inicializados se destruyen en el orden inverso a su construcción original.

Para cada elemento del array de tipo no-array U que debe ser destruido, se destruye como si fuera mediante pu - > ~U ( ) , donde pu es un puntero a ese elemento del array de tipo U .

(desde C++20)

Parámetros

args - lista de argumentos con los cuales se construirá un objeto de T
N - tamaño del array a utilizar
u - el valor inicial para inicializar cada elemento del array

Valor de retorno

std::shared_ptr a un objeto de tipo T o std:: remove_extent_t < T > [ N ] si T es un tipo de array sin límite (desde C++20) .

Para el std::shared_ptr r devuelto, r. get ( ) retorna un puntero no nulo y r. use_count ( ) retorna 1 .

Excepciones

Puede lanzar std::bad_alloc o cualquier excepción lanzada por el constructor de T . Si se lanza una excepción, las funciones no tienen efecto. Si se lanza una excepción durante la construcción del array, los elementos ya inicializados se destruyen en orden inverso. (desde C++20)

Notas

Estas funciones normalmente asignarán más memoria que sizeof ( T ) para permitir estructuras internas de contabilidad como conteos de referencia.

Estas funciones pueden utilizarse como alternativa a std:: shared_ptr < T > ( new T ( args... ) ) . Las compensaciones son:

  • std:: shared_ptr < T > ( new T ( args... ) ) realiza al menos dos asignaciones de memoria (una para el objeto T y otra para el bloque de control del puntero compartido), mientras que std :: make_shared < T > típicamente realiza solo una asignación (el estándar recomienda, pero no requiere esto; todas las implementaciones conocidas lo hacen).
  • Si algún std::weak_ptr referencia el bloque de control creado por std::make_shared después de que finalice el tiempo de vida de todos los propietarios compartidos, la memoria ocupada por T persiste hasta que todos los propietarios débiles también sean destruidos, lo cual puede ser indeseable si sizeof ( T ) es grande.
  • std:: shared_ptr < T > ( new T ( args... ) ) puede llamar a un constructor no público de T si se ejecuta en un contexto donde es accesible, mientras que std::make_shared requiere acceso público al constructor seleccionado.
  • A diferencia de los constructores de std::shared_ptr , std::make_shared no permite un eliminador personalizado.
  • std::make_shared utiliza :: new , por lo que si se ha configurado algún comportamiento especial utilizando un operator new específico de clase, diferirá de std:: shared_ptr < T > ( new T ( args... ) ) .
(hasta C++20)
  • código como f ( std:: shared_ptr < int > ( new int ( 42 ) ) , g ( ) ) puede causar una fuga de memoria si g se llama después de new int ( 42 ) y lanza una excepción, mientras que f ( std :: make_shared < int > ( 42 ) , g ( ) ) es seguro, ya que dos llamadas a función nunca se intercalan .
(hasta C++17)

Un constructor habilita shared_from_this con un puntero ptr de tipo U* significa que determina si U tiene una clase base inequívoca y accesible (desde C++17) que sea una especialización de std::enable_shared_from_this , y si es así, el constructor evalúa 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 ) ) ;
.

La asignación a weak_this no es atómica y entra en conflicto con cualquier acceso potencialmente concurrente al mismo objeto. Esto garantiza que futuras llamadas a shared_from_this() compartirían la propiedad con el std::shared_ptr creado por este constructor de puntero en bruto.

La prueba ptr - > weak_this  . expired ( ) en el código anterior asegura que weak_this no sea reasignado si ya indica un propietario. Esta prueba es requerida a partir de C++17.

Macro de prueba de características Valor Std Característica
__cpp_lib_shared_ptr_arrays 201707L (C++20) Soporte para arrays de std::make_shared ; sobrecargas ( 2-5 )
__cpp_lib_smart_ptr_for_overwrite 202002L (C++20) Creación de punteros inteligentes con inicialización por defecto ( std::allocate_shared_for_overwrite , std::make_shared_for_overwrite , std::make_unique_for_overwrite ); sobrecargas ( 6,7 )

Ejemplo

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
struct C
{
    // constructores necesarios (hasta C++20)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};
int main()
{
    // usando "auto" para el tipo de "sp1"
    auto sp1 = std::make_shared<C>(1); // sobrecarga (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
    // siendo explícito con el tipo de “sp2”
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // sobrecarga (1)
    static_assert(std::is_same_v<decltype(sp2), std::shared_ptr<C>>);
    static_assert(std::is_same_v<decltype(sp1), decltype(sp2)>);
    std::cout << "sp2->{ i:" << sp2->i << ", f:" << sp2->f << " }\n";
    // shared_ptr a un float[64] inicializado por valor; sobrecarga (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
    // shared_ptr a un long[5][3][4] inicializado por valor; sobrecarga (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
    // shared_ptr a un short[128] con inicialización de valor; sobrecarga (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
    // shared_ptr a un int[7][6][5] inicializado por valor; sobrecarga (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
    // shared_ptr a un double[256], donde cada elemento es 2.0; sobrecarga (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
    // shared_ptr a un double[7][2], donde cada double[2]
    // elemento es {3.0, 4.0}; sobrecarga (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
    // shared_ptr a un vector<int>[4], donde cada vector
    // tiene contenido {5, 6}; sobrecarga (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
    // shared_ptr a un float[512], donde cada elemento es 1.0; sobrecarga (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
    // shared_ptr a un double[6][2], donde cada elemento double[2]
    // es {1.0, 2.0}; sobrecarga (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
    // shared_ptr a un vector<int>[4], donde cada vector
    // tiene contenido {5, 6}; sobrecarga (5):
    std::shared_ptr<std::vector<int>[4]> spC =
        std::make_shared<std::vector<int>[4]>({5, 6});
}

Salida:

sp1->{ i:1, f:0 }
sp2->{ i:2, f:3 }

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
LWG 4024 C++20 no estaba claro cómo se destruyen los objetos construidos en
std::make_shared_for_overwrite
aclarado

Véase también

construye un nuevo shared_ptr
(función miembro pública)
crea un puntero compartido que gestiona un nuevo objeto asignado usando un asignador
(plantilla de función)
permite que un objeto cree un shared_ptr que haga referencia a sí mismo
(plantilla de clase)
crea un puntero único que gestiona un nuevo objeto
(plantilla de función)
funciones de asignación
(función)