【问题标题】:Understanding of Scott Meyers' third example of std::weak_ptr理解 Scott Meyers 的第三个 std::weak_ptr 例子
【发布时间】:2021-03-02 14:36:04
【问题描述】:

Effective Modern C++ 第 137 页的最后一个示例描绘了一个数据结构场景,其中包含对象 ABC,通过 @ 相互连接987654324@的方式如下:

   std::shared_ptr       std::shared_ptr
A ─────────────────▶ B ◀───────────────── C

对我来说,这意味着对象AC(通常是两个不相关的类)必须包含std::shared_ptr<classOfB> 成员。

然后假设我们需要一个从B 回到A 的指针,并且列出了可用的选项:该指针可以是原始的、共享的或弱的,最后一个作为最佳人选。

   std::shared_ptr       std::shared_ptr
A ─────────────────▶ B ◀───────────────── C
▲                    │
│    std::weak_ptr   │
└────────────────────┘

我确实理解前两个替代方案的弱点(啊哈哈),但我也看到第三个替代方案要求成员 A 已经由一些 std::shared_ptr 管理,否则怎么能std::weak_ptr 指向它?

然而这本书并没有提到这个“限制”/假设/无论如何,所以事实是

  • 我错了
  • 我是对的,但出于某种我不明白的原因,这种假设很明显
  • 这个假设是显而易见的,因为 std::weak_ptr 需要一个已经存在的 std::shared_ptr 到同一个对象,但有点奇怪,在示例的开头甚至没有提到它,我想。

我问这个问题是为了理解这一点。

【问题讨论】:

  • 如果A 不被std::shared_ptr 处理,则没有替代方案...
  • 我认为这个假设是公平的。 weak_ptr 在这里仅用作修复 shared_ptr 循环的方法。要么你已经通过共享指针管理了 A 的生命周期,在这种情况下你想要使用弱指针,或者你根本没有考虑智能指针,在这种情况下 B 无法确保 A 是活动的反正。在任何情况下都没有其他选择。
  • @IllidanS4supportsMonica,我没想到这一点。请随时将其发布为答案。

标签: c++ c++11 shared-ptr weak-ptr effective-c++


【解决方案1】:

你是对的,你的第三个要点是正确的。 std::weak_ptr 始终引用现有的 std::shared_ptr。所以,一旦我们做出决定 A 将包含 shared_ptr<BClass> 并且 A 的实例被管理为 shared_ptr<AClass> 的,我们可以使用 weak_ptr<AClass> 从 B 到 A 的反向引用。

在一个典型的用例中,你可以有一个 D 类管理三个成员 shared_ptr<AClass> a;shared_ptr<BClass> b;shared_ptr<CClass> c;,然后 D 类中的一些成员函数执行 a->SetB(b);c->SetB(b);b->SetA(a);,SetB 成员函数使用 shared_ptr 和 SetA 使用 weak_ptr(或在成员函数中转换为 weak_ptr)。正如你所说的那样,如果 D 确实以任何其他方式存储了对 A 的引用,例如原始指针 AClass* a; 或实例 AClass a;,那么使用weak_ptr 根本不可能。

【讨论】:

  • 我对您回答中的第一个 always 表示怀疑,因为在第 134 页,Scott 写道 _std::weak_ptrs 通常是从 std::shared_ptrs 创建的。也许他不典型地暗示复制std::weak_ptr(通过复制ctor)。
【解决方案2】:

std::shared_ptr 设计的不幸后果是共享指针管理的依赖图中不能出现循环。这意味着一旦 A 通过共享指针指向 B,B 就不能以相同的方式指向 A,因为这会导致内存泄漏(两个对象都将保持活动状态)。

std::weak_ptr 的主要目的是作为弱引用,但大多数时候,它仅用于修复此问题。但是,如果您首先不通过共享指针管理 A 的生命周期,那么 B 无论如何都无法跟踪它,因此使用原始指针、引用(或其他一些外来指针)是唯一的选择。相反,如果您通过共享指针拥有 A,则 weak_ptr 是唯一的选择。

在这两种情况下,选择完全取决于您之前管理 A 的决定,这是您必须在这里做的事情(可能通过同时引用 A 和 C 的对象)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-02
    • 2018-05-14
    • 2014-12-17
    • 2018-01-31
    • 2016-10-18
    • 1970-01-01
    • 1970-01-01
    • 2019-11-25
    相关资源
    最近更新 更多