我是否了解shared_ptr
不需要使用引用计数的新标准权限?只是它很可能是以这种方式实现的吗?
我可以想象一个以某种方式使用隐藏链表的实现。在 N3291 “20.7.2.2.5.(8) shared_ptr 观察者 [util.smartptr.shared.obs]” 注释说
[注意:use_count() 不一定有效。——尾注]
这给了我这个想法。
我是否了解shared_ptr
不需要使用引用计数的新标准权限?只是它很可能是以这种方式实现的吗?
我可以想象一个以某种方式使用隐藏链表的实现。在 N3291 “20.7.2.2.5.(8) shared_ptr 观察者 [util.smartptr.shared.obs]” 注释说
[注意:use_count() 不一定有效。——尾注]
这给了我这个想法。
你是对的,规范中没有任何内容需要使用明确的“计数器”,并且存在其他可能性。
例如,建议使用链表实现来实现boost's shared_ptr
;然而,该提案最终被否决,因为它在其他领域(大小、复制操作和线程安全)引入了成本。
有人说shared_ptr
是“引用计数器智能指针”。我不认为这是看待它的正确方式。
实际上shared_ptr
是关于(非排他的)所有权:所有 用指针初始化shared_ptr
的副本都是所有者。shared_ptr
p
shared_ptr
跟踪所有者集合,以保证:
delete p
不调用delete p
的副本)立即被调用;D
当然,要确定所有者集合何时变为空,shared_ptr
只需要一个计数器。抽象的描述更容易思考。
为了跟踪所有者的数量,计数器不仅是最明显的方法,而且相对明显的是如何使用原子比较和修改来使线程安全。
为了跟踪所有所有者,所有者的链表不仅是显而易见的解决方案,而且也是避免为每组所有者分配任何内存的简单方法。问题是要使这种方法有效地线程安全并不容易(任何东西都可以通过全局锁实现线程安全,这与并行性的想法背道而驰)。
一方面,我们有一个小的、固定大小的(除非使用自定义销毁函数)内存分配,这很容易优化,以及简单的整数原子操作。
另一方面,链表处理成本高且复杂;如果需要每个所有者设置的互斥锁(我认为是这样),内存分配的成本又回来了,此时我们可以用计数器替换互斥锁!
我读过多少次“标准”类可能有许多实现?
谁没有听说过可以实现为极坐标的复杂类的幻想?众所周知,这是愚蠢的。complex必须使用笛卡尔坐标。如果首选极坐标,则必须创建另一个类。极地复杂类不可能被用作普通复杂类的替代品。
对于(非标准)字符串类也是如此:字符串类没有理由在内部被 NUL 终止并且不将长度存储为整数,只是为了重复调用的乐趣和低效率strlen
。
我们现在知道设计std::string
容忍 COW 是一个坏主意,这就是 const 迭代器异常无效语义的原因。
std::vector
现在保证是连续的。
在某些时候,必须放弃标准类具有许多明显不同的合理实现的幻想。标准类是原始的构建块;它们不仅应该非常有效,而且应该具有可预测的效率。
程序员应该能够对基本操作的相对速度做出可移植的假设。如果即使是最简单的加法也变成一堆超越计算,那么复杂的类对于严重的数字运算也是无用的。如果不能保证字符串类通过数据共享具有非常快速的复制,程序员将不得不最小化字符串复制。
只有在不使普通的廉价操作变得非常昂贵(通过比较)时,实现者才可以自由选择不同的实现技术。
对于许多类来说,这意味着只有一种可行的实现策略,有时具有一些自由度(例如 a 中块的大小std::deque
)。