【发布时间】:2020-05-12 17:22:40
【问题描述】:
我最近遇到了一个问题,unique_ptr 和 shared_ptr 似乎都不是正确的解决方案。
所以,我正在考虑发明另一种智能指针(如下所述),但我心想“我肯定不是第一个想要这个的人。”
所以我的高级问题是:
- 下面的设计有意义吗?
- 有没有办法通过现有的智能 ptrs(或其他
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 -----+
my_ptr 的接口与unique_ptr 基本相同。
在内部,它将存储一个指向“控制块”的指针,该指针实际上只是“真实”指针和控制块本身的引用计数。
在销毁时,my_ptr 会将控制块指针设置为 NULL 并减少 refcount(并在适当的情况下删除控制块)。
my_weak_ptr 是可复制的,并且有一些get() 方法可以返回真正的Obj*。
用户将负责在使用它之前检查它是否为 NULL。
在销毁时,my_weak_ptr 会减少计数(并删除控制块,如果合适的话)。
缺点是每次访问都会在内存中进行两次跳跃。
对于my_ptr,也可以通过在内部存储真正的Obj* 来缓解这种情况,但my_weak_ptr 引用总是需要支付双跳成本。
编辑:一些相关问题,来自给出的链接:
- Non-ownership copies of std::unique_ptr
- Is it possible / desirable to create non-copyable shared pointer analogue (to enable weak_ptr tracking / borrow-type semantics)?
- Better shared_ptr by distinct types for "ownership" and "reference"?
所以似乎有这样的需求,但没有灌篮解决方案。如果需要线程安全,shared_ptr 和 weak_ptr 是正确的选择,但如果不需要,它们会增加不必要的开销。
还有boost::local_scoped_ptr,不过还是共享所有权模式;我宁愿阻止拥有指针的副本,例如unique_ptr。
【问题讨论】:
-
您将如何为您的班级实施
weak_ptr::lock?为什么不直接使用shared_ptr和weak_ptr?重新发明轮子有什么好处? -
@WernerHenze 如果你不需要线程安全,那么你就不需要
lock。您只需检查对象是否还活着,然后在其他任何事情发生之前使用它。 -
你能不能只使用指向
my_ptr的普通指针并在使用之前查询它是否仍然指向任何东西? -
get()非常糟糕,因为 c++ 是并发的,并且在您执行get()之后,对象可能会被销毁。你不应该在 c++ 中访问你不拥有的指针(即使是暂时的) -
@jwd 其他线程可以做的任何事情,您从线程调用的函数都可以做。如果您只调用不会导致对象被破坏的函数,那么只要您的代码正在运行,您就可以延长它的寿命。这就是共享所有权的定义,你说你不想要。但您实际上确实想要共享所有权。只要您使用
get返回的指针,您就需要保证该对象不会消失。无论您如何获得该保证,这就是共享所有权。所以只需使用shared_ptr。
标签: c++ c++14 smart-pointers