【问题标题】:What variables does weak_ptr hold?weak_ptr 持有哪些变量?
【发布时间】:2019-09-26 16:21:00
【问题描述】:

我了解哪些方法可用以及它们是什么。请描述weak_ptr 类的私有部分或给出一些自定义weak_ptr 代码的示例。我无法通过 std::weak_ptr 实现来理解。

【问题讨论】:

    标签: c++ pointers weak-ptr


    【解决方案1】:

    非侵入式共享指针实现通常包含指向某个动态分配的“状态”的指针,它计算对原始对象的引用数。

    当一个共享指针被拷贝时,拷贝得到同一个指向同一个“状态”的指针,“状态”内的计数递增表示现在有两个共享指针共享资源。

    当共享指针被销毁时,它递减计数器以指示现在共享资源的指针减少了。如果这导致计数器读数为零,则资源被销毁。

    弱指针也有一个指向这个“状态”的指针,但它递增或递减计数器。当被询问时,它将使用相同的状态构造一个共享指针,但前提是计数不为零。如果计数为零,则最后一个共享指针已经破坏了资源,我们无法再访问它。

    有趣的是,您还需要这样的逻辑来控制“状态”对象的生命周期。 :) (我想这是使用第二个计数器实现的,shared_ptrweak_ptr 都会增加,但不要引用我的话。)

    (your data)         (ref. counters)
         ║                    ║
    [resource]             [state]
      ┆  │ │                │ │ │
      ┆  │ └─[shared_ptr]───┘ │ │
      ┆  └───[shared_ptr]─────┘ │
      └┄┄┄┄┄┄┄[weak_ptr]────────┘
    

    当然,任何特定 std::weak_ptr 实现的私有部分的具体外观取决于编写它的人。

    顺便说一句,如果您怀疑它指向的资源可能已经shared_ptr( s) 其他地方:你会得到第二个不相关的“状态”对象,你的计数器会出错,你的资源可能会过早地被销毁(如果存在这样的概念,肯定会被销毁两次),造成混乱。

    【讨论】:

      【解决方案2】:

      为了理解链接的共享-弱对,您必须首先想象一个没有任何“弱引用”功能的纯共享“智能指针”。为了提高效率,想象它是一个侵入性的拥有引用计数指针:计数器是托管对象的一部分。

      回到“弱引用”特性的实现:我们需要一个能够检测所指对象是否还活着并(原子地)绑定到它的真实(强)引用的对象。 “仍然活着”的测量需要在始终存在的引用计数上完成(直到它不再需要,当有零引用,强或弱时),所以“弱引用”需要是能够保持那个计数器活着。因此,“弱引用”是对侵入式计数数据结构的拥有(强)引用,该结构保持外部计数数据结构的引用计数,T 类型的托管对象weak_ptr<T>

      当没有更多真实(强)引用(shared_ptr)时,实现必须销毁T 类型的托管对象,因此它必须有一个原子计数器用于此目的。请注意,从技术上讲,它不一定是某种原子整数,但 atomic<int> 恰好是获得这种效果的一种简单方法。操作是:

      • 由其他所有者的副本创建所有者(shared_ptrshared_ptr):以原子方式递增计数
      • 所有者销毁和测试:原子递减并测试最后一个所有者(即计数为零)
      • 从弱引用创建所有者(weak_ptrshared_ptr):原子测试计数是否非零,然后增加并指示成功,否则不修改计数并指示失败

      所有这些操作都可以在原子变量上进行。它们都涉及原子 RMW(读取修改写入),这通常比简单的读取或写入要昂贵得多。但是,如果该位置在本地 L1d 缓存中可用并且不在 CPU 之间反弹,则成本会更低。

      另一个计数是针对侵入式计数的对象:弱引用的计数必须分开,因为即使T 类型的对象已被销毁,它也可以不为零。

      对管理数据结构的计数的唯一操作是递增和递减和测试,因为对于稳定的对象,永远无法达到零值:零表示正在销毁的对象。

      当然,通常只有一个词可以原子操作,每个计数器通常是一个词,因为在某些情况下半个词可能会溢出,但一个词不能(因为你没有内存来创建 2 ** 31对象,甚至 2 ** 63 对于 64 位机器);所以对计数器的修改有限制。一个简单的解决方案是,侵入式引用计数不跟踪强所有者的更改,这已经很好地保存在一个计数器中,不需要在另一个计数器中复制。侵入式计数器仅用于对T 的最后一个强引用和对T 的弱引用(此处为weak_ptr),它们也是对管理数据结构的强引用。

      所以有两个原子计数器:

      • 强引用计数:确定T 何时被销毁(以及它的内存被释放,除非分配与make_shared 一样与管理数据共享时
      • (弱引用加上最后一个强引用)计数:表示存在多少引用,所有强引用计为一个

      其他变体可能是可能的,如果您放弃线程安全,您可以获得更多创意(您可以想象所有者的链接列表),但这些可能不会更有效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-08-26
        • 2021-07-05
        • 2010-12-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多