unique_ptr smart shared_ptr pointer make_shared example c++ pointers c++11 shared-ptr

c++ - smart - Cómo usar un shared_ptr con un puntero a la estructura que no debe ser liberado



unique pointer (3)

Actualmente estoy usando algunas funciones de la biblioteca glib. Con glib también viene el gio. glib es una biblioteca de C y, por lo tanto, necesito eliminar algunas estructuras que creo.

para muchos de los objetos creo un puntero inteligente, por ejemplo:

std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref);

Para esto crea un puntero compartido a un GAsyncQueue y esto destruye la cola de forma segura en el final de su vida útil.

Sin embargo, encuentro un problema cuando obtengo un puntero de la biblioteca de gio que no debería liberar. En el siguiente código, my_connection es un GSocketClient que implementa (en glib speak) GIOStream.

std::shared_ptr<GInputStream> my_input_stream = std::shared_ptr<GInputStream> ( g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) );

Debido a que la documentación en GIOStream menciona, el puntero obtenido con g_io_stream_get_input_stream() no debe liberarse. Esto se debe a que es propiedad de la instancia my_connection . Pensé en crear una lamda para el objeto de destrucción, el segundo parámetro de un objeto de puntero compartido. por ejemplo, auto deleter = [](GInputStream* ptr) {}; y luego otorga esa función lambda como de destrucción al puntero compartido, pero eso se siente una especie de estupidez.


Bueno, la alternativa a un borrado no operativo podría estar utilizando el aliasing puntero compartido

template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

Comparte x , pero después de obtener () volverás p .

Discusión: ¿Para qué es el constructor de aliasing de shared_ptr?


Probablemente no necesites un std::shared_ptr . Y probablemente ni siquiera necesitas un puntero.

Mientras leo tu pregunta y comentarios, no veo ningún punto en contra

auto& my_input_stream = *( g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) )

Es cierto que los punteros permiten datos opcionales. Sin embargo, también es cierto que se utiliza principalmente de forma incorrecta. Teniendo

void foo( type* ptr) { if (!ptr) throw exception; }

a menudo no tiene sentido Si la función tiene que trabajar en datos concretos, permitir un parámetro NULO solo es útil si luego se preocupa por proporcionar esos datos. De lo contrario, solo necesita una referencia (posiblemente const ) al objeto.

Los punteros inteligentes son útiles; pero siguen siendo punteros. Evitarlos por completo, si es posible, es incluso mejor.

De los comentarios:

Sin embargo, una referencia siempre debe ser inicializada.

Absolutamente. Desde C ++ 11, sin embargo, tenemos std::reference_wrapper que también puede ser modificado y almacenado en contenedores.


Puede usar un tipo de eliminación que no hace nada, pero tendrá que ser pasado como un argumento al constructor shared_ptr

struct DoNothing { template <typename T> void operator()(T*) const noexcept { } };

Al crear un shared_ptr , deberá crear uno de estos borradores y pasarlo al constructor (como lo está haciendo con la lambda). Puedes hacerte esto más fácil con una función intermedia.

template <typename T> std::shared_ptr<T> non_deleting_shared_ptr(T* ptr) { return {ptr, DoNothing}; } auto my_input_stream = non_deleting_shared_ptr( g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()));

Sin embargo, la pregunta más importante es por qué utiliza punteros inteligentes cuando no quiere que la propiedad sea parte de ella. Es casi seguro que estarías mejor con solo un GAsyncQueue* , a menos que, por supuesto, te encuentres en una situación en la que tengas shared_ptr que necesita ser liberada a veces . Como un miembro de datos tal vez?