簡體   English   中英

std :: shared_ptr中的最大引用計數是多少? 如果你試圖超過它會發生什么?

[英]What is the maximum reference count in std::shared_ptr? What happens if you try to exceed it?

如果我們假設std::shared_ptr存儲了一個引用計數(我意識到標准不需要,但我不知道任何沒有的實現),那個引用計數的位數有限,這意味着有支持的最大引用數。 這導致了兩個問題:

  • 這個最大值是多少?
  • 如果你試圖超過它會發生什么(例如,通過復制引用具有最大引用計數的對象的std :: shared_ptr)? 請注意, std::shared_ptr的復制構造函數聲明為noexcept

該標准是否對這兩個問題都有所了解? 常見的實現如何,例如gcc,MSVC,Boost?

我們可以從shared_ptr::use_count()函數中獲取一些信息。 §20.7.2.2.5說:

long use_count() const noexcept;

返回: shared_ptr對象的數量, *this包括*this對象,與*this共享所有權,或者當*this為空時為0

[注意: use_count()不一定有效。-end note]

乍一看, long回報類型似乎回答了第一個問題。 然而,這個注釋似乎暗示shared_ptr可以自由地使用它想要的任何類型的引用計數,包括像引用列表之類的東西。 如果是這種情況,那么理論上就沒有最大參考計數(盡管肯定存在實際限制)。

沒有其他參考限制我可以找到的同一對象的引用數量。

值得注意的是, use_count被記錄為不拋出和(顯然)正確報告計數; 除非實施確實使用long成員進行計數,否則我看不出這兩種方法在任何時候都能在理論上得到保證。

我不確定標准的含義,但實際上看看它:

引用計數很可能是某種std::size_t變量。 此變量在32位環境中最多可保持-1+2^32 ,在64位環境中最多可保持-1+2^64

現在想象這個變量要達到這個值會發生什么:你需要2 ^ 32或2 ^ 64個shared_ptr實例。 好多啊。 事實上,在你達到這個數字之前很久就會耗盡所有內存,因為一個shared_ptr大約是8/16字節。

因此,如果refcount變量的大小足夠大,則您不太可能達到引用計數的限制。

標准沒有說; 正如你所說,它甚至不需要引用計數。 另一方面,標准(或至少在C標准中)存在(或曾經)超出實施限制的陳述是未定義的行為。 所以這幾乎肯定是官方的答案。

在實踐中,我希望大多數實現將計數保持為size_tptrdiff_t 在具有平面尋址的機器上,這幾乎意味着您無法創建足夠的引用來導致溢出。 (在這樣的機器上,單個對象可以占用所有內存,而size_tptrdiff_t具有與指針相同的大小。由於每個引用計數指針都有一個不同的地址,所以永遠不會超過指針。)然而,在具有分段架構的機器上,可以想象溢出。

正如Jon指出的那樣,標准還要求std::shared_ptr::use_count()返回long 我不確定這里的基本原理: size_tptrdiff_t在這里會更有意義。 但是,如果實現對引用計數使用不同的類型,可能會將轉換為long的規則應用:“如果可以在目標類型(和位字段寬度)中表示,則值不會更改;否則,值是實現定義的。“ (C標准使這一點更加清晰:“實現定義的值”可以是一個信號。)

您可以通過使用placement new實例化共享指針並從不刪除它們來了解將會發生什么。 然后,您可以輕松地達到32位限制。

在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. 如果您有溢出和釋放所有權,您最終可能會遇到一些免費使用后的問題

由於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所有者之間共享一個對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM