简体   繁体   English

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

[英]Should I use unique_ptr or shared_ptr in this case?

in the mainwindow of my QT app, I use a std::shared_ptr to hold a pointer to an instance of my network service which manages all the connections to multiple clients. 在我的QT应用程序的主窗口中,我使用std::shared_ptr来保存指向网络服务实例的指针,该实例管理与多个客户端的所有连接。 Now, I have to pass this pointer to multiple sub windows so that they can communicate with the clients. 现在,我必须将此指针传递给多个子窗口,以便它们可以与客户端进行通信。

Do i better use a std::shared_ptr member variable in the main- and sub-windows and pass copy it when creating the subwindows, or is it better to use a std::unique_ptr and pass a raw pointer to the sub Windows, as the mainwindow will outlive the subwindows anyway? 我最好在主窗口和子窗口中使用std::shared_ptr成员变量,并在创建子窗口时通过复制它,还是更好地使用std::unique_ptr并将原始指针传递给子Windows,如主窗口仍然会超过子窗口?

Thanks a lot! 非常感谢!

The main practical difference is what happens when the mainwindow is destroyed while a subwindow still exists and is using the network service: 主要的实际区别是当子窗口仍然存在并且正在使用网络服务时,主窗口被破坏时会发生什么:

  • If you use unique_ptr and pass raw pointers then you get undefined behavior. 如果您使用unique_ptr并传递原始指针,那么您将获得未定义的行为。
  • If you use shared_ptr then the network service remains until all subwindows are destroyed. 如果您使用shared_ptr则网络服务将保留到所有子窗口都被销毁为止。

Now, if this condition is impossible by design then the undefined behavior is not inherently a problem. 现在,如果这种条件在设计上是不可能的,那么不确定的行为本质上就不是问题。 If the condition happens anyway due to a bug then it might help you to detect the bug if the network service is destroyed along with the main window, which would happen if you use unique_ptr . 如果这种情况由于错误而仍然发生,那么如果网络服务与主窗口一起被破坏,则可能会帮助您检测到该错误,如果您使用unique_ptr ,则会发生这种情况。 Using unique_ptr expresses that the mainwindow is the only thing that owns the network service, the others merely use it as directed by the mainwindow. 使用unique_ptr表示主窗口是唯一拥有网络服务的东西,其他人仅按照主窗口的指示使用它。

On the other hand, if you later change the design and want to make the condition legal, or if you want to use the subwindows in a different way that means there's no single network service object that they all use and that outlives them all, then it will be easier if you used shared_ptr from the start. 另一方面,如果您以后更改设计并希望使条件合法,或者如果您想以不同的方式使用子窗口,则意味着它们都不会使用一个单独的网络服务对象,并且所有对象都不会使用,那么如果您从一开始就使用了shared_ptr ,它将更加容易。 Using shared_ptr expresses that all the windows share ownership of the network service. 使用shared_ptr表示所有窗口都共享网络服务的所有权。

I don't think it's possible to say in general whether you should try to make your code continue working in the face of "impossible conditions". 我认为一般来说,是否应该在面对“不可能的条件”时尝试使代码继续工作是不可能的。 In this case it's very cheap to do so ( shared_ptr is more expensive to copy than a raw pointer, of course, but cheap compared with creating a subwindow, and the code is structured the same either way). 在这种情况下,这样做非常便宜(当然, shared_ptr复制比原始指针要昂贵,但是与创建子窗口相比,复制要便宜,并且代码的结构相同)。 It can be useful to have the flexibility to make the "impossible condition" happen under certain circumstances, for example when unit testing the subwindow code. 灵活地使“不可能的条件”在某些情况下发生是很有用的,例如在对子窗口代码进行单元测试时。 So probably use shared_ptr for flexibility. 因此,可以使用shared_ptr来提高灵活性。 If enforcing the lifetime constraint is a big deal for other reasons then you might not want any flexibility, in which case avoid writing code that will only ever hide bugs. 如果由于其他原因而强制执行生存期约束很重要,那么您可能不希望有任何灵活性,在这种情况下,请避免编写只会隐藏错误的代码。

Actually, there is a 3rd alternative you have not explored. 实际上,您还没有探索过第三种选择。

  • Passing shared_ptr allows you to have multiple owners, however it seems in your case this is unnecessary 传递shared_ptr可以让您拥有多个所有者,但是在您看来,这是不必要的
  • Using unique_ptr and passing a raw pointer enforces a single owner, but in case of bug then you are faced with undefined behavior (a crash). 使用unique_ptr并传递原始指针将强制执行一个所有者,但是如果发生错误,则您将面临不确定的行为(崩溃)。

The 3rd alternative is to combine both approaches using weak_ptr : 第三种选择是使用weak_ptr结合两种方法:

  • The main window owns the device in a shared_ptr 主窗口在shared_ptr 拥有设备
  • The sub windows are handed down a weak_ptr 子窗口传递了一个weak_ptr

When the sub window needs to communicate using the device, they will use lock() on the weak_ptr to temporarily obtain a shared_ptr . 当子窗口需要使用设备进行通信时,它们将在weak_ptr上使用lock()临时获取shared_ptr You can then assert or throw if the shared_ptr is empty (it's a bug), and otherwise use it to communicate with the device, and then let it go as you are done. 然后,您可以断言抛出 shared_ptr是否为空(这是一个错误),否则可以使用它与设备进行通信,然后在完成时将其释放。


EDIT : as Steve Jessop noted another assertion is useful (and achievable): ensuring that when the main window destroys the shared_ptr it was the last owner and the device is released (to protect against an accidental leak of ownership). 编辑 :正如史蒂夫·杰索普(Steve Jessop)指出的那样,另一个断言是有用的(并且可以实现):确保在主窗口销毁shared_ptr它是最后的所有者,并且设备被释放(以防止所有权意外泄漏)。

The naive way, asserting that it's unique before destruction, unfortunately suffers from a race-condition; 不幸的是,幼稚的方式断言它在销毁之前是unique ,因而遭受了种族条件的折磨。 between the call to unique and the actual destruction, a call to a weak_ptr::lock could create a new owner. 在调用unique销毁和实际销毁之间,调用weak_ptr::lock可以创建一个新所有者。

It can be done simply, however: 可以简单地完成,但是:

  1. Create a new weak_ptr called monitor from your shared_ptr . 从您的shared_ptr创建一个名为monitor的新的weak_ptr
  2. Reset the shared_ptr . 重置shared_ptr
  3. Call monitor.lock() , if it returns a non-null shared_ptr then there is an owner in the wild. 调用monitor.lock() ,如果它返回一个非null的shared_ptr则说明存在一个所有者。

The question about using std::shared_ptr or std::unique_ptr is mostly one of ownership. 有关使用std::shared_ptrstd::unique_ptr的问题主要是所有权之一。 Will there be only one single owner at a time to the object? 一次只能有一个所有者吗? Or will there be multiple owners to the object? 还是该对象有多个所有者?

In your case it seems like you want multiple owners, so then std::shared_ptr is the way to go. 在您的情况下,您似乎想要多个所有者,因此采用std::shared_ptrstd::shared_ptr的方法。

Don't use std::unique_ptr and then pass around the raw wrapped pointer. 不要使用std::unique_ptr然后传递原始包装的指针。 It will come back to bite you when you forget it and the std::unique_ptr object goes out of scope while someone else still have access to the raw pointer. 当您忘记它并且std::unique_ptr对象超出范围,而其他人仍然可以访问原始指针时,它将再次咬住您。

It sounds to me like the network service is an object which should exist for the life of your program. 在我看来,网络服务是在您的程序生命期内应存在的对象。 In which case, it's not certain that any type of smart pointer should be used; 在这种情况下,不能确定应使用任何类型的智能指针。 the most obvious solution would be a local variable in main . 最明显的解决方案是main的局部变量。 At any rate, the first thing to ask yourself is what the lifetime of the object should be. 无论如何,首先要问自己的是对象的寿命应该是多少。 If that lifetime corresponds to the scope of main (or the scope of any other function), the local variable is the way to go. 如果该生存期与main的范围(或任何其他函数的范围)相对应,则应采用局部变量。 If that lifetime should correspond to that of the main window, a member variable of the main window would be the appropriate solution. 如果该生存期应与主窗口的生存期相对应,则主窗口的成员变量将是适当的解决方案。 It all depends on how your application specifies the object lifetimes (which is a design issue, which can't be solved by low level programming techniques). 这完全取决于您的应用程序如何指定对象生存期(这是一个设计问题,无法通过低级编程技术解决)。

About the only case you might need to use pointers is if the type is polymorphic, and the actual type is not known until runtime (eg because it is determined by entries in a configuration file). 关于唯一的情况,您可能需要使用指针,如果类型是多态的,并且直到运行时才知道实际类型(例如,因为它是由配置文件中的条目确定的)。 In this case, you'll have to manage the lifetime yourself, but if it does correspond to a scope, std::unique_ptr might be a good solution, since if not moved, it precisely emulates the lifetime of a scoped variable. 在这种情况下,您必须自己管理生命周期,但是如果确实与作用域相对应,则std::unique_ptr可能是一个很好的解决方案,因为如果不移动,它将精确地模拟作用域变量的生命周期。 (I'd be very sceptical of std::shared_ptr here; the lifetime should probably be deterministic, and not dependent on whether someone happens to hold a pointer to it. I can also imagine scenarios where the network server would hold pointers to its clients, with a risk of cycles.) (我对std::shared_ptr非常怀疑;生存期应该是确定性的,而不取决于是否有人持有指向它的指针。我还可以想象网络服务器将持有指向其客户端的指针的情况,但存在循环风险。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM