的shared_ptr<>不要求使用引用计数?

时间:2011-09-11 12:20:04

标签: shared-ptr c++11 reference-counting

我是否理解使用引用计数shared_ptr不是必需的新标准权限?只有它可能以这种方式实现?

我可以想象一个以某种方式使用隐藏链表的实现。在N3291“20.7.2.2.5。(8)shared_ptr观察者[util.smartptr.shared.obs]”说明

  

[注意:use_count()不一定有效。 - 结束说明]

这给了我这个想法。

2 个答案:

答案 0 :(得分:5)

你是对的,规范中没有任何内容要求使用明确的“计数器”,并且存在其他可能性。

例如,链接列表实现是shared_ptr的{​​{3}};但是,该提案最终被拒绝,因为它引入了其他领域的成本(规模,复制操作和线程安全)。

答案 1 :(得分:5)

摘要说明

有人说shared_ptr是“参考计数器智能指针”。我不认为这是看待它的正确方法。

实际上 shared_ptr完全是(非独占)所有权:所有shared_ptr都是shared_ptr的副本,用指针{{1}初始化所有者

p跟踪所有者,以保证:

  • 虽然所有者集非空,但未调用shared_ptr
  • 当所有者集合为空时,会立即调用delete p(或delete p销毁仿函数的副本);

当然,要确定所有者集合何时变空,D只需要一个计数器。抽象描述稍微容易思考。

可能的实现技术

为了跟踪所有者的数量,计数器不仅是最明显的方法,使用原子比较和修改如何使线程安全也是相对明显的。

为了跟踪所有所有者,所有者的链接列表不仅是明显的解决方案,而且还是一种避免为每组所有者分配任何内存的简单方法。问题是,使这种方法有效线程安全并不容易(任何事情都可以通过全局锁定使线程安全,这违背了并行的想法)。

在多线程实现的情况下

一方面,我们有一个小的,修复大小(除非使用自定义销毁函数)内存分配,非常容易优化,以及简单的整数原子操作。

另一方面,链接列表处理成本高昂且复杂;如果需要每个所有者设置互斥量(我认为是这样),内存分配的成本又回来了,此时我们可以用计数器替换互斥锁!

关于多种可能的实施

我读过多少次“标准”类可以实现多种实现?

谁从未听过这种可以作为极坐标实现的复杂类的幻想?众所周知,这是愚蠢的。复数必须使用笛卡尔坐标。如果首选极坐标,则必须创建另一个类。极地复杂类不可能被用作通常复杂类的替代品。

对于(非标准)字符串类也是如此:没有理由让字符串类在内部终止NUL并且不将长度存储为整数,只是为了反复调用shared_ptr的乐趣和低效率

我们现在知道设计strlen以容忍COW是一个坏主意,这是const迭代器不寻常的失效语义的原因。

std::string现在保证是连续的。

幻想的终结

在某些时候,必须放弃标准类有许多明显不同的合理实现的幻想。标准类是原始构建块;不仅它们应该非常有效,它们应该具有可预测的效率。

程序员应该能够对基本操作的相对速度做出可移植的假设。如果即使最简单的加法变成一组超越计算,复杂类对于严重的数字运算也是无用的。如果不保证字符串类通过数据共享具有非常快的副本,则程序员必须最小化字符串副本。

实施者可以自由选择不同的实施技术,只有当它不能使普通的廉价操作成本极高时(相比之下)。

对于许多类来说,这意味着只有一个可行的实现策略,有时会有一些自由度(比如std::vector中块的大小)。