2

While it is easy to learn about std::shared_ptr, std::unique_ptr and std::weak_ptr by knowing what are they, it seems to be very difficult for me to understand in what circumstances they proved to be useful.

Can anyone give one or two concrete example of three of them?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
getsuha
  • 711
  • 6
  • 16
  • Use `unique_ptr` when only one thing should own the pointer. Use `std::shared_ptr` if you want multiple things to share ownership. Use `weak_ptr` to hand out a stub that can get you the pointer, if it hasn't already been destroyed. – NathanOliver Mar 23 '21 at 17:34
  • All you have to do is imagine a world with **only** raw pointers. `weak_ptr` helps solve circular reference problem. – selbie Mar 23 '21 at 17:42
  • 2
    In my opinion, best to use `std::unique_ptr` (along with *non-owning* raw pointers as function parameters), and avoid `std::shared_ptr`. Sometimes `std::shared_ptr` really is the best solution, and only in those cases would I consider it. In contrast to C# on the .NET platform, which all objects are much like having `std::shared_ptr` everywhere. [F.27](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-shared_ptr) for a shared_ptr reason and example. – Eljay Mar 23 '21 at 17:42
  • Not a complete duplicate because that question is only about shared/weak ones, but there’re many good answers there: https://stackoverflow.com/q/48834271/126995 – Soonts Mar 23 '21 at 19:05
  • Read [_Effective Modern C++_](https://www.oreilly.com/library/view/effective-modern-c/9781491908419/), the answer is there. – Enlico Mar 24 '21 at 09:36
  • One important use case of weak_pte that I came across just recently is this: https://stackoverflow.com/questions/27085782/how-to-break-shared-ptr-cyclic-reference-using-weak-ptr – getsuha Apr 05 '21 at 07:32

1 Answers1

3

I assume you know what each of these smart pointers do so I am not going to explain them. Instead I will give you examples of how I have used them in my own code or from libraries I have used that need them.

std::unique_ptr: Can be returned from an API to indicate that the resource needs to be managed by the caller and that it is not safe to share the object. For example, gRPC creates CompletionQueue instances like this. CompletionQueue has sensitive lifetime and synchronization requirements. By returning unique_ptr it is clear that the caller is responsible for lifetime management and it makes it cumbersome to share the CompletionQueue. It is still not impossible to share it (can get the raw pointer from the unique_ptr or use a reference to the unique_ptr, both of which are typically code smells).

std::shared_ptr/std::weak_ptr: Not the simplest use case, but here we go...

AudioPlayer::AudioPlayer() {
   thread_local std::weak_ptr<AudioPlayerMixer> mixedOutput;

   if (mixedOutput.expired()) {
      m_mixedOutput = std::make_shared<AudioPlayerMixer>();
      mixedOutput = m_mixedOutput;
   } else {
      m_mixedOutput = mixedOutput.lock();
   }
}

In this example, all AudioPlayer instances on a thread need to have their audio mixed together using AudioPlayerMixer. So, each AudioPlayer has a shared_ptr<AudioPlayerMixer> so that when the last AudioPlayer instance holding the shared_ptr is destroyed, the mixer will be as well. The usage of thread_local just facilitates having a unique AudioPlayerMixer per thread. The thread_local storage of the AudioPlayerMixer should not prolong the life of the mixer as the lifetime of the AudioPlayerMixer is decided by the existence of AudioPlayer instances (ie. no AudioPlayer => no AudioPlayerMixer) - this is why a weak_ptr is used. mixedOutput.expired() == true means that either no AudioPlayer has been created on this thread or all previously existing players were deleted. mixedOutput.expired() == false means another AudioPlayer exists on this thread and the current AudioPlayer needs to get a shared_ptr to the same mixer. Note you have to be careful when using shared_ptr and weak_ptr from multiple threads as they are not thread safe. In this example, thread_local and the fact that AudioPlayer is only created and destroyed within the same thread ensures it is safe.

Of course, this could have all be done without the smart pointers but the smart pointers help communicate the intent of the code better.

Dean Johnson
  • 1,682
  • 7
  • 12
  • 1
    If the goal is to get a `shared_ptr` from a `weak_ptr` it is usually safer and better to just use `.lock()` and then to check if the produced `shared_ptr` actually points to something or not, instead of using `expired` first. `lock()` returns a default constructed instance if it is expired. – François Andrieux Mar 23 '21 at 18:22
  • @FrançoisAndrieux Good point, thanks for the simplification. – Dean Johnson Mar 23 '21 at 18:31