理解 Scott Meyers 的第三个例子 std::weak_ptr

Understanding of Scott Meyers' third example of std::weak_ptr

Effective Modern C++ 第 137 页的最后一个示例绘制了包含对象 AB 和 [=15 的数据结构的场景=] 在其中,通过 std::shared_ptr 以下列方式相互连接:

   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怎么可能指向它呢?

然而书中并没有提到这个“限制”/assumption/whatever,所以事实是

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

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

在典型的用例中,您可以有一个 D-class 来管理三个成员 shared_ptr<AClass> a;shared_ptr<BClass> b;shared_ptr<CClass> c;,然后是一些成员函数D-class 执行 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 根本就不是可能。

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

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

在这两种情况下,选择完全取决于您之前管理 A 的决定,这是您必须在此处执行的操作(可能通过引用 A 和 C 的对象)。