
[英]Calling a method of a *shared_ptr* - what happens to reference count?
[英]What is the maximum reference count in std::shared_ptr? What happens if you try to exceed it?
如果我們假設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_t
或ptrdiff_t
。 在具有平面尋址的機器上,這幾乎意味着您無法創建足夠的引用來導致溢出。 (在這樣的機器上,單個對象可以占用所有內存,而size_t
或ptrdiff_t
具有與指針相同的大小。由於每個引用計數指針都有一個不同的地址,所以永遠不會超過指針。)然而,在具有分段架構的機器上,可以想象溢出。
正如Jon指出的那樣,標准還要求std::shared_ptr::use_count()
返回long
。 我不確定這里的基本原理: size_t
或ptrdiff_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位計數器,不檢查溢出。
這意味着:
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
所有者之間共享一個對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.