Если мы предположим, что 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
, или же0
когда*this
пустой.use_count()
не обязательно эффективен.
На первый взгляд long
Тип возврата, кажется, отвечает на первый вопрос. Однако примечание, кажется, подразумевает, что shared_ptr
может свободно использовать любой тип подсчета ссылок, который он хочет, включая такие вещи, как список ссылок. Если бы это было так, то теоретически не было бы максимального количества ссылок (хотя, безусловно, было бы практическое предел).
Нет другой ссылки на ограничение количества ссылок на тот же объект, который я мог бы найти.
Интересно отметить, что use_count
задокументировано как не выбрасывать, так и (очевидно) правильно сообщать счет; если реализация не использует long
член для подсчета Я не понимаю, как оба из них могут быть теоретически гарантированы в любое время.
Я не уверен, что предлагает стандарт, но посмотрите на это практически:
счетчик ссылок скорее всего какой-то std::size_t
переменная. Эта переменная может содержать значения до -1+2^32
в 32-битных средах и до -1+2^64
в 64-битных средах.
Теперь представьте, что должно произойти, чтобы эта переменная достигла этого значения: вам потребуется 2 ^ 32 или 2 ^ 64 shared_ptr
экземпляров. Это много. На самом деле, это так много, что вся память будет исчерпана задолго до того, как вы достигнете этого числа, так как shared_ptr
размером около 8/16 байт.
Поэтому вы вряд ли сможете достичь предела количества ссылок, если размер переменной refcount достаточно велик.
Стандарт не говорит; как вы говорите, это даже не требует ссылки
считая. С другой стороны, есть (или было) утверждение в
стандарт (или, по крайней мере, в стандарте C), который превышает реализацию
пределы неопределенного поведения. Так что это почти наверняка официальный
ответ.
На практике я бы ожидал, что большинство реализаций будет поддерживать счет
как size_t
или ptrdiff_t
, На машинах с плоской адресацией это
в значительной степени означает, что вы не можете создать достаточно ссылок, чтобы вызвать
переполнение. (На таких машинах один объект может занимать все
память и size_t
или же ptrdiff_t
имеют тот же размер, что и указатель.
Поскольку каждый указатель с подсчетом ссылок имеет отдельный адрес, он может
никогда не будет больше, чем уместится в указателе.) На машинах с сегментированным
архитектура, однако, переполнение вполне возможно.
Как указывает Джон, стандарт также требует
std::shared_ptr::use_count()
вернуть long
, Я не уверен что
обоснование здесь: либо size_t
или же ptrdiff_t
сделает больше
Смысл здесь. Но если реализация использует другой тип для
счетчик ссылок, предположительно, правила для преобразования в long
было бы
apply: «значение не изменяется, если оно может быть представлено в
тип назначения (и ширина битового поля); в противном случае значение
определяется реализацией. «(Стандарт C делает это несколько яснее:
«значение, определяемое реализацией» может быть сигналом.)
Вы можете узнать, что произойдет, создавая общие указатели с помощью размещения новых и никогда не удаляя их. Затем вы можете легко достичь 32-битного лимита.
Стандарт C ++ 11 определяет long
как тип возвращаемого значения use_count()
функция наблюдателя, но не указывает явно, должна ли реализация поддерживать до 2^(sizeof(long)*8-1)-1
долевое владение
Также не указывается, что происходит, когда счетчик ссылок переполняется.
boost::shared_ptr
Реализация (например, 1.58 в Fedora 23, 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;
}
Смотрите также:
Реализация shared_pointer в GNU STL (libstdc ++) основана на Boost 1.32 и также имеет эту проблему (в Fedora 23 / x86-64) — там _Atomic_word
Тип используется для подсчета ссылок. Он также является «только» 32-битным и не проверяется на переполнение.
В отличие от LLVM libc ++ shared_ptr
реализация использует long
в качестве счетчика ссылок, то есть на платформах LP64, таких как x86-64, вы можете разделить объект до 2^63-1
владельцы.