我是否理解使用引用计数的shared_ptr
不是必需的新标准权限?只有它可能以这种方式实现?
我可以想象一个以某种方式使用隐藏链表的实现。在N3291“20.7.2.2.5。(8)shared_ptr观察者[util.smartptr.shared.obs]”说明
[注意:use_count()不一定有效。 - 结束说明]
这给了我这个想法。
答案 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
中块的大小)。