getsuha
getsuha

Reputation: 711

What are best use cases of shared_ptr, unique_ptr and weak_ptr?

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?

Upvotes: 2

Views: 2147

Answers (1)

Dean Johnson
Dean Johnson

Reputation: 1852

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.

Upvotes: 3

Related Questions