Reputation: 711
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
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