std :: shared_ptr中的最大引用计数是多少?如果你试图超过它会发生什么?

时间:2012-09-13 08:17:43

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

如果我们假设std::shared_ptr存储引用计数(我意识到标准不需要,但我不知道任何没有的实现),那引用计数的位数有限,并且这意味着支持的引用数量最多。这导致了两个问题:

  • 这个最大值是多少?
  • 如果你试图超过它会发生什么(例如,通过复制引用具有最大引用计数的对象的std :: shared_ptr)?请注意,std::shared_ptr的复制构造函数声明为noexcept

该标准是否对这两个问题都有所了解?常见的实现如何,例如,gcc,MSVC,Boost?

5 个答案:

答案 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_tptrdiff_t。在具有平面寻址的机器上,这个 几乎意味着你无法创建足够的引用来导致 溢出。 (在这样的机器上,一个物体可以占据所有的物体 内存,size_tptrdiff_t与指针大小相同。 由于每个引用计数指针都有一个不同的地址,因此可以 永远不会超过适合指针。)在具有分段的机器上 然而,架构是可以想象的溢出。

Jon指出,该标准也需要 std::shared_ptr::use_count()返回long。我不确定是什么 基本原理在这里:size_tptrdiff_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位计数器,不检查溢出。

这意味着:

  1. 最大引用次数为2^31-1
  2. 如果您有溢出和释放所有权,您最终可能会遇到一些免费使用后的问题
  3. 由于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>