另一种类型的智能 ptr,比如带有弱引用的 unique_ptr?

jwd*_*jwd 6 c++ smart-pointers c++14

我最近遇到了一个问题,既不是unique_ptr也不shared_ptr是正确的解决方案。所以,我正在考虑发明另一种智能 ptr(如下所述),但我心里想“我当然不是第一个想要这个的人”。

所以我的高级问题是:

  • 下面的设计有意义吗?
  • 是否有某种方法可以使用现有的智能 ptr(或其他std::功能)来实现这一点,也许我遗漏了什么?

要求:

  • 我想要单一所有权,很像 unique_ptr
    • 也就是说:只有当单个拥有指针死亡时,才应该释放底层对象(与shared_ptr的行为不同)。
  • 我想要一些额外的方法来引用对象被删除时“知道”的对象。因此,类似于weak_ptr, 但要与单一所有权模型一起使用。
  • 我也没有需要线程安全

激励示例:

假设我正在迭代一个接口指针列表,在它们上调用方法。其中一些方法可能会导致列表中稍后的项目被删除。

使用普通指针,我会得到那些已删除项目的悬空引用。

建议设计:

让我们调用拥有指针my_ptr和非拥有引用my_weak_ptr。

对于给定的对象,我们可能有这样的图表:

                             _______
my_ptr<Obj> owner ---------> |Obj* | -------> [Obj data ... ]
                      +----> |count|
                      | +--> |_____|
my_weak_ptr<Obj> A ---+ |
                        |
my_weak_ptr<Obj> B -----+
Run Code Online (Sandbox Code Playgroud)

my_ptr将有一个与unique_ptr. 在内部,它将存储一个指向“控制块”的指针,该指针实际上只是“真实”指针和控制块本身的引用计数。在销毁时,my_ptr将控制块指针设置为 NULL 并减少引用计数(并删除控制块,如果合适)。

my_weak_ptr将是可复制的,并且有一些get()方法可以返回真实的Obj*. 用户将负责在使用它之前检查它是否为 NULL。在销毁时,my_weak_ptr将减少计数(并删除控制块,如果合适)。

缺点是每次访问都要通过内存两跳。对于my_ptr,这也可以通过在Obj*内部存储 true 来缓解,但my_weak_ptr引用将始终必须支付双跳成本。


编辑:一些相关问题,来自给出的链接:

所以似乎有这样的需求,但没有灌篮解决方案。如果需要线程安全shared_ptr并且weak_ptr是正确的选择,但如果不需要,它们会增加不必要的开销。

也有boost::local_scoped_ptr,但仍然是共享所有权模式;我宁愿阻止拥有指针的副本,例如unique_ptr.

jwd*_*jwd 4

上面的评论中有一些很好的讨论,所以我将尝试回答我自己的问题并总结:

首先,整个概念有一个总体缺点:任何用户都my_weak_ptr需要非常小心,不要调用某些可能导致底层对象被删除的函数。或者如果他们这样做了,他们需要重新检查弱指针是否为空。这是对用户施加的非强制(且无法强制)约束,就像他们使用原始指针一样。

话虽这么说:这不是新领域。在随后的研究中,我发现了这种想法的各种体现:

  • VISH StrongPtr/WeakPtr
    • 非常合适。请注意,它WeakPtr没有lock()方法,文档中说“如果引用的对象从其他地方被销毁,弱指针会神奇地变为空”。
    • 然而,它仍然是可复制的,因此并不表达独特的所有权。
  • 洛基强者
    • 通过适当使用令人眼花缭乱的策略选择(或者可能是自定义策略),我认为我所描述的内容是可以实现的。
  • 可追踪指针
    • 一种不同的方法,因为您T必须包装为trackable<T>,但类似的问题正在解决。

还有一些“相当不错但不太理想”的解决方案更接近std:

  • 使用shared_ptr和weak_ptr.
    • 缺点:线程安全开销、可复制的所有者 ptr、多个引用计数。
  • boost::local_shared_ptr,它与weak_ptr.
    • 缺点:可复制所有者 ptr、多个引用计数。

可能local_shared_ptr是最好的开箱即用解决方案,具有高品质和很少的缺点。

然而,为了真正挤出最后几个字节并禁止复制,需要一个自定义解决方案。

除此之外,更哲学地说:

从这里的讨论和其他阅读中,我感觉到许多人相信所有权的二元方法:要么是共享的(因此使用shared_ptr,这也为您提供了通过 共享的观察weak_ptr),要么是唯一的(因此使用unique_ptr)。

这可能涵盖了 90% 以上的案例。然而,我想要具有共同观察的独特所有权(这是我的措辞;您可能会根据您的语义使用不同的单词)。可能太极端了,无法被标准涵盖,但我认为对于资源受限的系统来说,这似乎是一个合理的利基市场。