如何将 shared_ptr 与指向不应释放的结构的指针一起使用
How to use a shared_ptr with a pointer to struct that should not be freed
目前我正在使用 glib 库中的一些函数。 glib 还带有 gio。 glib 是一个 C 库,因此我需要删除我创建的一些结构。
我为许多对象创建了一个智能指针,例如:
std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref);
为此创建了一个指向 GAsyncQueue
的共享指针,并且在其生命周期结束时安全地销毁了队列。
但是,当我从 gio 库中获取一个我不应该释放的指针时遇到了问题。在下面的代码中,my_connection
是一个 GSocketClient,它实现了(用油嘴滑舌的话说)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
实例。
我考虑过为 destroy 对象创建一个 lamda,共享指针对象的第二个参数。例如 auto deleter = [](GInputStream* ptr) {};
然后将该 lambda 作为销毁函数提供给共享指针,但这感觉有点愚蠢。
您可以使用不执行任何操作的删除器类型,但需要将其作为参数传递给 shared_ptr
的构造函数
struct DoNothing {
template <typename T>
void operator()(T*) const noexcept { }
};
创建 shared_ptr
时,您需要创建其中一个删除器并将其传递到构造函数中(就像您对 lambda 所做的那样)。您可以使用中间函数
让自己更轻松
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
需要释放 有时 .也许像数据成员?
好吧,无操作删除器的替代方法可能是使用别名共享指针
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
它共享 x
,但在 get() 之后您将返回 p
。
讨论:What is shared_ptr's aliasing constructor for?
您可能只是不需要 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
)。
智能指针很有用;但它们仍然是指针。如果可能的话,完全避免它们会更好。
来自评论:
However, a reference must always be initialized
当然可以。从 C++11 开始,虽然我们有 std::reference_wrapper
也可以重新评估并存储在容器中。
目前我正在使用 glib 库中的一些函数。 glib 还带有 gio。 glib 是一个 C 库,因此我需要删除我创建的一些结构。
我为许多对象创建了一个智能指针,例如:
std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref);
为此创建了一个指向 GAsyncQueue
的共享指针,并且在其生命周期结束时安全地销毁了队列。
但是,当我从 gio 库中获取一个我不应该释放的指针时遇到了问题。在下面的代码中,my_connection
是一个 GSocketClient,它实现了(用油嘴滑舌的话说)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
实例。
我考虑过为 destroy 对象创建一个 lamda,共享指针对象的第二个参数。例如 auto deleter = [](GInputStream* ptr) {};
然后将该 lambda 作为销毁函数提供给共享指针,但这感觉有点愚蠢。
您可以使用不执行任何操作的删除器类型,但需要将其作为参数传递给 shared_ptr
的构造函数
struct DoNothing {
template <typename T>
void operator()(T*) const noexcept { }
};
创建 shared_ptr
时,您需要创建其中一个删除器并将其传递到构造函数中(就像您对 lambda 所做的那样)。您可以使用中间函数
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
需要释放 有时 .也许像数据成员?
好吧,无操作删除器的替代方法可能是使用别名共享指针
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
它共享 x
,但在 get() 之后您将返回 p
。
讨论:What is shared_ptr's aliasing constructor for?
您可能只是不需要 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
)。
智能指针很有用;但它们仍然是指针。如果可能的话,完全避免它们会更好。
来自评论:
However, a reference must always be initialized
当然可以。从 C++11 开始,虽然我们有 std::reference_wrapper
也可以重新评估并存储在容器中。