如何将shared_ptr与指向不应释放的结构的指针一起使用

How to use a shared_ptr with a pointer to struct that should not be freed

本文关键字:释放 结构 指针 一起 shared ptr      更新时间:2023-10-16

目前我正在使用glib库中的一些函数。油嘴滑舌的还有焦音。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,它实现(用glib语言)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作为destroy函数提供给共享指针,但这感觉有点愚蠢。

好吧,没有操作删除程序的替代方案可能是使用别名共享指针

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

它共享x,但在get()之后,您将返回p

讨论:什么是shared_ptr';的别名构造函数?

您可能不需要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)。

智能指针很有用;但它们仍然是指针。如果可能的话,完全避免它们会更好。


来自评论:

但是,引用必须始终初始化

当然。从C++11开始,我们有了std::reference_wrapper,它也可以重新连接并存储在容器中。

您可以使用一个什么都不做的deleter类型,但它需要作为参数传递给shared_ptr的构造函数

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

创建shared_ptr时,您需要创建其中一个deleter,并将其传递给构造函数(就像您对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有时需要释放。也许像一个数据成员?