weak_ptr smart shared_ptr pointer example create c++ c++11 smart-pointers make-shared

c++ - smart - ¿Qué sucede cuando se utiliza make_shared?



unique pointer (3)

El primer caso no realiza una asignación doble, realiza dos asignaciones, una para el objeto gestionado y otra para el bloque de control de shared_ptr .

Para el segundo caso, cppreference tiene una buena explicación de por qué std::make_shared usualmente solo realiza una asignación de memoria que dice ( énfasis en el futuro ):

Esta función generalmente asigna memoria para el objeto T y para el bloque de control shared_ptr con una sola asignación de memoria (es un requisito no vinculante en el Estándar) . En contraste, la declaración std :: shared_ptr p (nueva T (Args ...)) realiza al menos dos asignaciones de memoria, lo que puede generar una sobrecarga innecesaria.

y de la sección std::shared_ptr dice:

Cuando se crea shared_ptr llamando a std :: make_shared o std :: allocate_shared, la memoria para el bloque de control y el objeto administrado se crea con una sola asignación. El objeto gestionado se construye in situ en un miembro de datos del bloque de control. Cuando shared_ptr se crea a través de uno de los constructores shared_ptr, el objeto administrado y el bloque de control deben asignarse por separado. En este caso, el bloque de control almacena un puntero al objeto administrado.

Esta descripción de make_shared es consistente con el borrador de estándar de C ++ 11 que dice en la sección 20.7.2.2.6 shared_ptr creation

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);

[...]

Observaciones: Las implementaciones no deben realizar más de una asignación de memoria. [Nota: Esto proporciona una eficiencia equivalente a un puntero inteligente intrusivo. "Nota final"

[Nota: estas funciones generalmente asignarán más memoria que sizeof (T) para permitir estructuras de contabilidad internas como los recuentos de referencia. "Nota final"

Herb Sutter tiene una explicación más detallada de las ventajas de usar make_shared en herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers y señala algunas ventajas:

  • Se reduce la sobrecarga de asignación
  • Mejora la localidad.
  • Evita una explícita nueva.
  • Evita un problema de seguridad de excepción.

Tenga en cuenta que al usar std::weak_ptr con make_shared tiene algunas desventajas .

Me interesa si estas dos líneas de código son las mismas:

shared_ptr<int> sp(new int(1)); // double allocation? shared_ptr<int> sp(make_shared<int>(1)); // just one allocation?

Si esto es cierto, ¿podría alguien explicar por qué es solo una asignación en la segunda línea?


Explicación de std::shared_ptr en la sección Implementation notes

En una implementación típica, std :: shared_ptr contiene solo dos punteros:

  1. un puntero al objeto gestionado
  2. un puntero para controlar el bloque

Cuando se crea shared_ptr llamando a std :: make_shared o std :: allocate_shared, la memoria para el bloque de control y el objeto administrado se crea con una sola asignación. El objeto gestionado se construye in situ en un miembro de datos del bloque de control. Cuando shared_ptr se crea a través de uno de los constructores shared_ptr, el objeto administrado y el bloque de control deben asignarse por separado. En este caso, el bloque de control almacena un puntero al objeto administrado.


También hay un posible error sutil: en sp(new int) primero asignas un int (cuyo puntero se asigna a sp ), que sp tiene que asignar un bloque de control (contendrá los contadores y el eliminador).

Ahora, si al hacer esta última asignación sp falla (poca memoria), se queda con un montón asignado a int cuyo puntero no es mantenido por nadie, y por lo tanto es imposible de eliminar. (pérdida de memoria).