shared_ptr、unique_ptr 和weak_ptr 的最佳用例是什么?

问题描述

虽然通过了解 std::shared_ptrstd::unique_ptrstd::weak_ptr 很容易了解它们,但我似乎很难理解它们在什么情况下被证明是有用。

谁能举出其中三个的一两个具体例子?

解决方法

我假设你知道每个智能指针的作用,所以我不打算解释它们。相反,我将举例说明我如何在自己的代码中或从我使用过的需要它们的库中使用它们。

std::unique_ptr:可以从 API 返回,表示资源需要由调用者管理,共享对象不安全。例如,gRPC 创建 CompletionQueue 这样的实例。 CompletionQueue 具有敏感的生命周期和同步要求。通过返回 unique_ptr,很明显调用者负责生命周期管理,并且共享 CompletionQueue 变得很麻烦。共享它仍然不是不可能的(可以从 unique_ptr 获取原始指针或使用对 unique_ptr 的引用,这两者都是典型的代码异味)。

std::shared_ptr/std::weak_ptr:不是最简单的用例,但我们开始......

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();
   }
}

在此示例中,线程上的所有 AudioPlayer 实例都需要使用 AudioPlayerMixer 将其音频混合在一起。因此,每个 AudioPlayer 都有一个 shared_ptr<AudioPlayerMixer>,因此当最后一个持有 AudioPlayershared_ptr 实例被销毁时,混合器也会被销毁。 thread_local 的使用只是便于每个线程拥有唯一的 AudioPlayerMixerthread_localAudioPlayerMixer 存储不应延长混频器的寿命,因为 AudioPlayerMixer 的寿命取决于 AudioPlayer 实例的存在(即没有 {{ 1}} => 没有 AudioPlayer) - 这就是使用 AudioPlayerMixer 的原因。 weak_ptr 表示没有在此线程上创建 mixedOutput.expired() == true 或删除所有先前存在的玩家。 AudioPlayer 表示此线程上存在另一个 mixedOutput.expired() == false,当前 AudioPlayer 需要将 AudioPlayer 获取到同一个混频器。请注意,在多个线程中使用 shared_ptrshared_ptr 时必须小心,因为它们不是线程安全的。在此示例中,weak_ptr 以及 thread_local 仅在同一线程中创建和销毁的事实确保它是安全的。

当然,这一切都可以在没有智能指针的情况下完成,但智能指针有助于更好地传达代码的意图。