3

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

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

[注意:use_count() 不一定有效。——尾注]

这给了我这个想法。

4

2 回答 2

5

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

例如,建议使用链表实现来实现boost's shared_ptr;然而,该提案最终被否决,因为它在其他领域(大小、复制操作和线程安全)引入了成本。

于 2011-09-11T12:51:18.773 回答
5

摘要说明

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

实际上shared_ptr是关于(非排他的)所有权:所有 用指针初始化shared_ptr的副本都是所有者shared_ptrp

shared_ptr跟踪所有者集合,以保证:

  • 当所有者集合非空时,delete p不调用
  • 当所有者集合为空时, (或销毁函子delete p的副本)立即被调用;D

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

可能的实现技术

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

为了跟踪所有所有者,所有者的链表不仅是显而易见的解决方案,而且也是避免为每组所有者分配任何内存的简单方法。问题是要使这种方法有效地线程安全并不容易(任何东西都可以通过全局锁实现线程安全,这与并行性的想法背道而驰)。

在多线程实现的情况下

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

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

关于多种可能的实现

我读过多少次“标准”类可能有许多实现?

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

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

我们现在知道设计std::string容忍 COW 是一个坏主意,这就是 const 迭代器异常无效语义的原因。

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

幻想的终结

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

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

只有在不使普通的廉价操作变得非常昂贵(通过比较)时,实现者才可以自由选择不同的实现技术。

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

于 2011-10-14T02:56:50.713 回答