【问题标题】:How to use a shared_ptr with a pointer to struct that should not be freed如何将 shared_ptr 与指向不应释放的结构的指针一起使用
【发布时间】:2016-03-11 07:45:09
【问题描述】:

目前我正在使用 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,它实现了(在 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 实例所有。 我想过为销毁对象创建一个lamda,它是共享指针对象的第二个参数。例如auto deleter = [](GInputStream* ptr) {};,然后将该 lambda 作为销毁函数提供给共享指针,但这感觉有点愚蠢。

【问题讨论】:

  • 为什么要使用(智能)指针?引用还不够吗?
  • @black 我还在考虑它。输入流是被复制的对象的一个​​实例。 GIOStream 在调用最后一个复制析构函数时被销毁。也许因为无论如何我都不需要销毁它,所以一个普通的旧指针也可以...
  • 还有一个指针我可以设置为NULL,这样就比较容易看是否初始化了。

标签: c++ pointers c++11 shared-ptr


【解决方案1】:

您可以使用不执行任何操作的删除器类型,但需要将其作为参数传递给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 需要有时释放的情况。可能像数据成员?

【讨论】:

    【解决方案2】:

    好吧,无操作删除器的替代方法可能是使用别名共享指针

    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?

    【讨论】:

      【解决方案3】:

      您可能只是不需要std::shared_ptr。而且你可能甚至不需要指针。

      当我阅读您的问题和 cmets 时,我认为没有任何反对意见

      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)。

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


      来自cmets:

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

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

      【讨论】:

      • 根据我的问题,您是对的。但是,必须始终初始化引用。由于 my_input_stream 是未打开的类成员(未在我的问题中显示)。我只有在实际打开套接字连接时才能设置它。但我会投票赞成,因为它对其他读者来说是一个很好的答案。
      • @hetepeperfan 是的。看看我的更新能否解决这个问题;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-03
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多