在这种情况下,我应该使用unique_ptr还是shared_ptr
Should I use unique_ptr or shared_ptr in this case?
在我的QT应用程序的主窗口中,我使用std::shared_ptr
来保存指向我的网络服务实例的指针,该实例管理到多个客户端的所有连接。现在,我必须将这个指针传递给多个子窗口,以便它们能够与客户端通信。
我最好在主窗口和子窗口中使用std::shared_ptr
成员变量,并在创建子窗口时传递副本,还是最好使用std::unique_ptr
并传递指向子窗口的原始指针,因为主窗口无论如何都会比子窗口更长?
非常感谢!
主要的实际区别是,当主窗口被破坏,而子窗口仍然存在并使用网络服务时会发生什么:
- 如果使用
unique_ptr
并传递原始指针,则会得到未定义的行为 - 如果使用
shared_ptr
,则网络服务将保留,直到所有子窗口都被销毁
现在,如果这个条件在设计上是不可能的,那么未定义的行为本质上就不是问题。如果这种情况是由于错误而发生的,那么如果网络服务与主窗口一起被破坏,可能会帮助您检测错误,如果您使用unique_ptr
,就会发生这种情况。使用unique_ptr
表示主窗口是唯一拥有网络服务的东西,其他人只是按照主窗口的指示使用它。
另一方面,如果您以后更改设计,并希望使条件合法,或者如果您希望以不同的方式使用子窗口,这意味着没有一个网络服务对象是它们都使用的,并且比它们都要长,那么如果您从一开始就使用shared_ptr
会更容易。使用shared_ptr
表示所有窗口共享网络服务的所有权。
我不认为在面对"不可能的条件"时,是否应该尝试让代码继续工作是可能的。在这种情况下,这样做非常便宜(当然,shared_ptr
的复制成本比原始指针更高,但与创建子窗口相比更便宜,而且代码的结构也相同)。在某些情况下,例如在单元测试子窗口代码时,具有使"不可能条件"发生的灵活性是有用的。所以可能使用shared_ptr
来获得灵活性。如果由于其他原因,强制执行生存期约束是一件大事,那么您可能不希望有任何灵活性,在这种情况下,避免编写只会隐藏错误的代码。
实际上,还有第三种选择您尚未探索。
- 通过
shared_ptr
允许您拥有多个所有者,但在您的情况下,这似乎是不必要的 - 使用
unique_ptr
并传递一个原始指针强制使用一个所有者,但在出现错误的情况下,您将面临未定义的行为(崩溃)
第三种选择是使用weak_ptr
:将两种方法结合起来
- 主窗口拥有
shared_ptr
中的设备 - 子窗口下发
weak_ptr
当子窗口需要使用设备进行通信时,他们将使用weak_ptr
上的lock()
来临时获得shared_ptr
。然后,如果shared_ptr
为空(这是一个错误),您可以断言或抛出,否则可以使用它与设备通信,然后让它继续运行。
EDIT:正如Steve Jessop所指出的,另一个断言是有用的(也是可以实现的):确保当主窗口破坏shared_ptr
时,它是最后一个所有者,并且设备被释放(以防止所有权的意外泄露)。
天真的方式,声称它在毁灭之前是unique
,不幸的是,它患有种族疾病;在对unique
的调用和实际销毁之间,对weak_ptr::lock
的调用可以创建新的所有者。
然而,它可以简单地完成:
- 从
shared_ptr
创建一个名为monitor
的新weak_ptr
- 重置
shared_ptr
- 调用
monitor.lock()
,如果它返回一个非空的shared_ptr
,则在野外有一个所有者
关于使用std::shared_ptr
或std::unique_ptr
的问题主要是所有权问题。对象一次只能有一个所有者吗?还是对象会有多个所有者?
在您的情况下,您似乎想要多个所有者,因此std::shared_ptr
是可行的。
不要
使用std::unique_ptr
,然后绕过原始换行指针。当你忘记它,std::unique_ptr
对象超出范围,而其他人仍然可以访问原始指针时,它会再次咬你。
在我看来,网络服务是一个应该在程序的生命周期中存在。在这种情况下不确定是否应该使用任何类型的智能指针;这个最明显的解决方案是CCD_ 36中的局部变量。在不管怎样,首先要问自己的是,你的一生是什么的。如果该生存期对应于main
的范围(或任何其他功能的范围),本地变量是前进的道路。如果该寿命应该对应对于主窗口的成员变量,主窗口的一个成员变量将是适当的解决方案。这完全取决于你应用程序指定对象的生存期(这是一种设计低级别编程无法解决的问题技术)。
您可能需要使用指针的唯一情况是类型是多态的,实际类型直到运行时(例如,因为它是由配置文件)。在这种情况下,您必须管理你自己的一生,但如果它确实对应于一个范围,CCD_ 38可能是一个好的解决方案,它精确地模拟了作用域变量的生存期。(我愿意对CCD_ 39持怀疑态度;寿命应该可能是确定性的,而不是取决于某人碰巧有一个指针指向它。我也可以想象场景其中网络服务器将保持指向其客户端的指针,具有周期风险。)
- enum是C++中的宏变量还是整数变量
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- 在命名空间中定义函数还是限定函数
- 架构决策:返回std::future还是提供回调
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 异常属于C++中的线程还是进程
- 在决定是通过参考还是通过价值时,尺寸真的是一个问题吗
- 如何在C++中确定文本文件中的元素是字符还是数字
- 返回值优化:显式移动还是隐式
- CLANG 编译器 说:变量"PTR"可能未初始化
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 为什么需要知道一个类是平凡的还是有平凡的构造函数
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- 'string.assign(string.data(), 5)' 是明确定义的还是 UB?
- 在哪里放置我的函数?进入我的母语 Gui 还是进入我的演示者?
- 在这种情况下,我真的复制了字节还是复制了字符?
- node-gyp 的先有鸡还是先有蛋的问题:指向依赖项中的头文件
- CIN是逻辑1还是0?