Как использовать shared_ptr с указателем на структуру, которая не должна быть освобождена
В настоящее время я использую некоторые функции из библиотеки glib. С glib также появляется gio. glib - это библиотека C, поэтому мне нужно удалить некоторые структуры, которые я создаю.
для многих объектов я создаю smartpointer, например:
std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref);
Для этого создается общий указатель на GAsyncQueue
, и это безопасно уничтожает очередь в конце ее жизни.
Однако я сталкиваюсь с проблемой, когда получаю указатель из библиотеки gio, который я не должен освобождать. В следующем коде my_connection
есть GSocketClient, который реализует (в glib-talk) GIOStream.
std::shared_ptr<GInputStream> my_input_stream =
std::shared_ptr<GInputStream> (
g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()))
);
Поскольку в документации по GIOStream упоминается, что указатель, полученный с помощью g_io_stream_get_input_stream()
, не должен быть освобожден. Это связано с тем, что он принадлежит экземпляру my_connection
.
Я думал о создании lamda для объекта destroy, второго параметра объекта общего указателя. например auto deleter = [](GInputStream* ptr) {};
, а затем дать эту лямбду как функцию detroy для общего указателя, но это кажется глупым.
Ответы
Ответ 1
Ну, альтернатива no-op deleter может использовать общий указатель наложения aliasing
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
Он разделяет x
, но после get() вы вернетесь p
.
Обсуждение: Что такое конструктор псевдонимов shared_ptr для?
Ответ 2
Вероятно, вам просто не нужен std::shared_ptr
. И вам, вероятно, даже не нужен указатель.
Когда я прочитал ваш вопрос и комментарии, я не вижу смысла против
auto& my_input_stream = *( g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) )
Верно, что указатели допускают дополнительные данные. Однако также верно, что он в основном использовался неправильно. Имея
void foo( type* ptr)
{
if (!ptr)
throw exception;
}
часто не имеет смысла. Если функция должна работать над конкретными данными, разрешение параметра NULL полезно, только если вы беспокоитесь о предоставлении этих данных. В противном случае просто требуется ссылка (возможно, const
) на объект.
Умные указатели полезны; но они все еще указатели. Избежать их вообще, если это возможно, еще лучше.
Из комментариев:
Однако ссылка всегда должна быть инициализирована
Совершенно верно. Начиная с С++ 11, хотя у нас есть std::reference_wrapper
, который также может быть повторно закреплен и сохранен в контейнерах.
Ответ 3
Вы можете использовать тип удаления, который ничего не делает, но его необходимо передать как аргумент конструктору shared_ptr
struct DoNothing {
template <typename T>
void operator()(T*) const noexcept { }
};
При создании shared_ptr
вам нужно будет создать один из этих удалений и передать его в конструкторе (как вы это делаете с лямбдой). Вы можете сделать это проще на себе с помощью промежуточной функции
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()));
Однако главный вопрос заключается в том, почему вы используете интеллектуальные указатели, когда вы не хотите, чтобы принадлежность к нему была частью. Вам почти наверняка будет лучше всего с GAsyncQueue*
, если, конечно, вы не находитесь в ситуации, когда у вас есть shared_ptr
, который иногда нужно освобождать. Как член данных, возможно?