如果我们假设std::shared_ptr
存储引用计数(我意识到标准不需要,但我不知道任何没有的实现),那引用计数的位数有限,并且这意味着支持的引用数量最多。这导致了两个问题:
std::shared_ptr
的复制构造函数声明为noexcept
。该标准是否对这两个问题都有所了解?常见的实现如何,例如,gcc,MSVC,Boost?
答案 0 :(得分:19)
我们可以从shared_ptr::use_count()
函数中获取一些信息。 §20.7.2.2.5说:
long use_count() const noexcept;
返回:共享的
shared_ptr
个对象的数量,*this
*this
的所有权,或0
为空时的*this
。[注意:
use_count()
不一定有效。-end note]
乍一看,long
返回类型似乎回答了第一个问题。然而,这个注释似乎意味着shared_ptr
可以自由地使用它想要的任何类型的引用计数,包括像引用列表之类的东西。如果是这种情况,那么理论上就没有最大引用计数(尽管肯定存在实际限制)。
没有其他参考限制我可以找到的同一对象的引用数量。
值得注意的是,use_count
被记录为不抛出和(显然)正确报告计数;除非实现确实使用了long
成员进行计数,否则我看不出这两者在理论上是如何保证的。
答案 1 :(得分:7)
我不确定标准是什么意思,但实际上看一下:
引用计数很可能是某种std::size_t
变量。此变量在32位环境中最多可以保存-1+2^32
,在64位环境中最多可以保存-1+2^64
。
现在想象这个变量要达到这个值会发生什么:你需要2 ^ 32或2 ^ 64 shared_ptr
个实例。好多啊。事实上,在你达到这个数字之前很久就会耗尽所有内存,因为一个shared_ptr
大约是8/16字节。
因此,如果refcount变量的大小足够大,则不太可能达到引用计数的限制。
答案 2 :(得分:7)
标准没有说;正如你所说,它甚至不需要参考 数数。另一方面,在(或曾经)有一个声明 超标的标准(或至少在C标准中) 限制是未定义的行为。所以这几乎肯定是官方的 答案。
在实践中,我希望大多数实现都能保持计数
作为size_t
或ptrdiff_t
。在具有平面寻址的机器上,这个
几乎意味着你无法创建足够的引用来导致
溢出。 (在这样的机器上,一个物体可以占据所有的物体
内存,size_t
或ptrdiff_t
与指针大小相同。
由于每个引用计数指针都有一个不同的地址,因此可以
永远不会超过适合指针。)在具有分段的机器上
然而,架构是可以想象的溢出。
Jon指出,该标准也需要
std::shared_ptr::use_count()
返回long
。我不确定是什么
基本原理在这里:size_t
或ptrdiff_t
会产生更多
感觉到这里。但是如果实现使用不同的类型
可能是引用计数转换为long
的规则
apply:“如果值可以表示,则值不变
目的地类型(和位字段宽度);否则,价值是
实现定义。“(C标准使这一点更加清晰:
“实现定义的值”可以是一个信号。)
答案 3 :(得分:2)
您可以通过使用placement new实例化共享指针并从不删除它们来了解会发生什么。然后,您可以轻松地达到32位限制。
答案 4 :(得分:0)
C ++ 11标准将long
指定为use_count()
观察者函数的返回类型,但未明确指定实现是否必须支持最多2^(sizeof(long)*8-1)-1
个共享所有权。
它也没有指定引用计数器溢出时会发生什么。
boost::shared_ptr
实现(例如Fedora 23上的1.58,x86-64)内部使用32位计数器,不检查溢出。
这意味着:
2^31-1
。由于boost对不同平台使用不同的低级特化,您可以通过在*add_ref_lock
中设置断点来验证详细信息 - 在Fedora 23 / x86-64上,您将停在此处:
/usr/include/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp
[..]
int use_count_; // #shared
int weak_count_; // #weak + (#shared != 0)
[..]
bool add_ref_lock() // true on success
{
return atomic_conditional_increment( &use_count_ ) != 0;
}
另见:
GNU STL(libstdc ++)shared_pointer实现基于Boost 1.32 one并且也有这个问题(在Fedora 23 / x86-64上) - _Atomic_word
类型用于引用计数。它也是'仅'32位,不检查溢出。
相比之下,LLVM libc ++ shared_ptr
实现使用long
作为参考计数器,即在x64-64等LP64平台上,您可以在最多2^63-1
所有者之间共享对象。 / p>