Какой максимальный счетчик ссылок в std :: shared_ptr? Что произойдет, если вы попытаетесь превзойти его?

Если мы предположим, что std::shared_ptr хранит счетчик ссылок (который, как я понимаю, стандарт не требует, но я не знаю о каких-либо реализациях, которые этого не делают), этот счетчик ссылок имеет ограниченное количество битов, и это означает, что существует максимальное количество поддерживаемых ссылок , Это приводит к двум вопросам:

  • Что это за максимальное значение?
  • Что произойдет, если вы попытаетесь превзойти его (например, скопировав std :: shared_ptr, который ссылается на объект с максимальным количеством ссылок)? Обратите внимание, что std::shared_ptrКопируется конструктор noexcept,

Стандарт проливает свет на любой из этих вопросов? Как насчет распространенных реализаций, например, gcc, MSVC, Boost?

20

Решение

Мы можем получить некоторую информацию от 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 член для подсчета Я не понимаю, как оба из них могут быть теоретически гарантированы в любое время.

19

Другие решения

Я не уверен, что предлагает стандарт, но посмотрите на это практически:

счетчик ссылок скорее всего какой-то std::size_t переменная. Эта переменная может содержать значения до -1+2^32 в 32-битных средах и до -1+2^64 в 64-битных средах.

Теперь представьте, что должно произойти, чтобы эта переменная достигла этого значения: вам потребуется 2 ^ 32 или 2 ^ 64 shared_ptr экземпляров. Это много. На самом деле, это так много, что вся память будет исчерпана задолго до того, как вы достигнете этого числа, так как shared_ptr размером около 8/16 байт.

Поэтому вы вряд ли сможете достичь предела количества ссылок, если размер переменной refcount достаточно велик.

7

Стандарт не говорит; как вы говорите, это даже не требует ссылки
считая. С другой стороны, есть (или было) утверждение в
стандарт (или, по крайней мере, в стандарте C), который превышает реализацию
пределы неопределенного поведения. Так что это почти наверняка официальный
ответ.

На практике я бы ожидал, что большинство реализаций будет поддерживать счет
как size_t или ptrdiff_t, На машинах с плоской адресацией это
в значительной степени означает, что вы не можете создать достаточно ссылок, чтобы вызвать
переполнение. (На таких машинах один объект может занимать все
память и size_t или же ptrdiff_t имеют тот же размер, что и указатель.
Поскольку каждый указатель с подсчетом ссылок имеет отдельный адрес, он может
никогда не будет больше, чем уместится в указателе.) На машинах с сегментированным
архитектура, однако, переполнение вполне возможно.

Как указывает Джон, стандарт также требует
std::shared_ptr::use_count() вернуть long, Я не уверен что
обоснование здесь: либо size_t или же ptrdiff_t сделает больше
Смысл здесь. Но если реализация использует другой тип для
счетчик ссылок, предположительно, правила для преобразования в long было бы
apply: «значение не изменяется, если оно может быть представлено в
тип назначения (и ширина битового поля); в противном случае значение
определяется реализацией. «(Стандарт C делает это несколько яснее:
«значение, определяемое реализацией» может быть сигналом.)

7

Вы можете узнать, что произойдет, создавая общие указатели с помощью размещения новых и никогда не удаляя их. Затем вы можете легко достичь 32-битного лимита.

2

Стандарт C ++ 11 определяет long как тип возвращаемого значения use_count() функция наблюдателя, но не указывает явно, должна ли реализация поддерживать до 2^(sizeof(long)*8-1)-1 долевое владение

Также не указывается, что происходит, когда счетчик ссылок переполняется.

boost::shared_ptr Реализация (например, 1.58 в Fedora 23, 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;
}

Смотрите также:

Реализация 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 владельцы.

0
По вопросам рекламы [email protected]