在这种情况下我应该使用unique_ptr还是shared_ptr?

时间:2014-02-18 09:06:32

标签: c++ shared-ptr unique-ptr

在我的QT应用程序的主窗口中,我使用std::shared_ptr来保存指向我的网络服务实例的指针,该实例管理到多个客户端的所有连接。现在,我必须将此指针传递给多个子窗口,以便它们可以与客户端通信。

我最好在主窗口和子窗口中使用std::shared_ptr成员变量,并在创建子窗口时传递它,或者更好地使用std::unique_ptr并将原始指针传递给子Windows,因为主窗口无论如何都会比子窗口更长?

非常感谢!

4 个答案:

答案 0 :(得分:8)

主要的实际区别是当主窗口被破坏而子窗口仍然存在并且正在使用网络服务时会发生什么:

  • 如果您使用unique_ptr并传递原始指针,那么您将获得未定义的行为。
  • 如果您使用shared_ptr,则网络服务将保留,直到所有子窗口都被销毁。

现在,如果设计不可能出现这种情况,那么未定义的行为本身就不是问题。如果由于某个错误而发生这种情况,那么可能可以帮助您检测网络服务与主窗口一起被破坏时的错误,如果您使用unique_ptr会发生这种情况。使用unique_ptr表示主窗口是唯一拥有网络服务的东西,其他人只是按照主窗口的指示使用它。

另一方面,如果您以后更改设计并希望条件合法,或者您希望以不同的方式使用子窗口,则意味着没有单个网络服务对象,他们都使用并且超过它们所有,如果你从一开始就使用shared_ptr会更容易。使用shared_ptr表示所有窗口共享网络服务的所有权。

我认为一般来说,是否可以尝试让代码继续在“不可能的条件”下继续工作。在这种情况下,这样做非常便宜(shared_ptr复制比原始指针更昂贵,当然,与创建子窗口相比便宜,并且代码的结构方式相同)。在某些情况下具有使“不可能条件”发生的灵活性是有用的,例如在单元测试子窗口代码时。因此可能使用shared_ptr来提高灵活性。如果由于其他原因强制执行生命周期约束是很重要的,那么您可能不会想要任何灵活性,在这种情况下,避免编写只会隐藏错误的代码。

答案 1 :(得分:6)

实际上,还有第三种选择,你还没有探索过。

  • 传递shared_ptr允许您拥有多个所有者,但在您的情况下似乎这是不必要的
  • 使用unique_ptr并传递原始指针会强制执行单个所有者,但如果出现错误,则会遇到未定义的行为(崩溃)。

第三种方法是使用weak_ptr组合两种方法:

  • 主窗口shared_ptr
  • 中拥有设备
  • 子窗口传递weak_ptr

当子窗口需要使用设备进行通信时,他们将使用lock()上的weak_ptr暂时 获取shared_ptr。如果shared_ptr为空(这是一个错误),你可以断言抛出,然后用它来与设备通信,然后让它去你完成了。


编辑:正如Steve Jessop所说,另一个断言是有用的(并且可以实现):确保当主窗口销毁shared_ptr时它是最后一个所有者并且设备被释放(到防止意外泄漏所有权。)

天真的方式,在破坏之前声称它是unique,不幸的是遭受了竞争条件;在致电unique和实际销毁之间,拨打weak_ptr::lock可能会创建新的所有者。

可以简单地完成:

  1. weak_ptr
  2. 创建一个名为monitor的新shared_ptr
  3. 重置shared_ptr
  4. 调用monitor.lock(),如果它返回非空shared_ptr,那么就会有一个拥有者。

答案 2 :(得分:4)

关于使用std::shared_ptrstd::unique_ptr的问题主要是所有权问题。一次只有一个单独的所有者到该对象?或者对象会有多个所有者吗?

在您的情况下,您似乎想要多个所有者,因此std::shared_ptr是可行的方式。

不要 使用std::unique_ptr然后传递原始包装指针。当你忘记它并且std::unique_ptr对象超出范围而其他人仍然可以访问原始指针时,它会回来咬你。

答案 3 :(得分:2)

听起来像网络服务是一个对象 应该存在于你的程序的生命周期。在这种情况下,它是 不确定应该使用任何类型的智能指针;该 最明显的解决方案是main中的局部变量。在 无论如何,首先要问自己的是一生 对象应该是。如果那个生命周期对应于 范围main(或任何其他功能的范围),本地 变量是要走的路。如果那个寿命应该对应 到主窗口的那个,是主窗口的成员变量 将是适当的解决方案。这一切都取决于你的方式 application指定对象的生命周期(这是一个设计 问题,低级编程无法解决 技术)。

关于唯一可能需要使用指针的情况是 type是多态的,直到实际类型才知道 运行时(例如因为它是由条目决定的) 配置文件)。在这种情况下,你将不得不管理 一辈子你自己,但如果它确实对应一个范围, std::unique_ptr可能是一个很好的解决方案,因为如果不移动, 它精确地模拟了作用域变量的生命周期。 (ID 对此std::shared_ptr持怀疑态度;一生应该 可能是确定性的,而不是依赖于某人 碰巧拿着指针。我也可以想象一下场景 网络服务器将指向其客户端的指针, 有周期风险。)