另一种类型的智能ptr,比如具有弱refs的unique_ptr

Another type of smart ptr, like unique_ptr with weak refs?

本文关键字:ptr refs unique 类型 智能 另一种      更新时间:2023-10-16

我最近遇到一个问题,unique_ptrshared_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 -----+

CCD_ 9将具有与CCD_。在内部,它将存储一个指向"控制块"的指针,该指针实际上只是控制块本身的"真实"指针和refcount。在销毁时,my_ptr会将控制块指针设置为NULL,并递减refcount(并在适当的情况下删除控制块)。

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

缺点是每次访问都要在内存中跳两次。对于my_ptr,也可以通过在内部存储真正的Obj*来减轻这种情况,但my_weak_ptr引用将始终需要支付双跳成本。


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

  • std::unique_ptr的非所有权副本
  • 是否可能/希望创建不可复制的共享指针模拟(以启用weak_ptr跟踪/借用类型语义)
  • 通过不同类型更好地共享ptr;所有权;以及";引用">

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

还有boost::local_scoped_ptr,但它仍然是一种共享所有权模式;我宁愿阻止拥有指针的副本,比如unique_ptr

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

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

话虽如此:这不是一个新领域。在随后的研究中,我发现了这样一个想法的各种化身:

  • VISH StrongPtr/WeakPtr
    • 非常合身。注意WeakPtr没有lock()方法;如果引用的对象从其他地方被破坏,弱指针就会神奇地变为null">
    • 然而,它仍然是可复制的,因此不表示唯一的所有权
  • Loki StrongPtr
    • 通过适当使用令人眼花缭乱的策略选择(或者可能是自定义策略),我认为我所描述的可以实现
  • 可跟踪_ptr
    • 一种不同的方法,因为您的T必须封装为trackable<T>,但类似的问题正在解决

还有一些";相当好但不太理想";更接近std:的解决方案

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

local_shared_ptr可能是最好的开箱即用解决方案,质量高,缺点少。

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

此外,更具哲学意义的是:

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

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

有一种设计不分配额外的块,而是将指针对象大小设置为3个指针。指针是双链表的一个节点,每个弱引用都是同一个链表的节点。

缺点是线性删除的复杂性(必须使每个引用无效),以及使其有效地实现线程安全的不可行性。

优点是快速取消对共享指针和弱指针的引用。

我不记得我到底在哪里见过或听到过这个想法。。。