shared_ptr <> не требуется использовать счетчик ссылок? - PullRequest
3 голосов
/ 11 сентября 2011

Понимаю ли я новое стандартное право, что shared_ptr не требуется для использования счетчика ссылок ? Только то, что вполне вероятно, что это реализовано таким образом?

Я мог бы представить реализацию, которая каким-то образом использует скрытый связанный список. В N3291 «20.7.2.2.5. (8) наблюдатели shared_ptr [util.smartptr.shared.obs]» В записке написано

[Примечание: use_count () не обязательно эффективен. - конец примечания]

, который дал мне эту идею.

Ответы [ 2 ]

5 голосов
/ 14 октября 2011

Абстрактное описание

Некоторые люди говорят, что shared_ptr - это «умный указатель счетчика ссылок».Я не думаю, что это правильный способ взглянуть на это.

На самом деле shared_ptr - это все (неисключительное) владение : все shared_ptr, которые являются копиямиshared_ptr инициализируется указателем p являются владельцами .

shared_ptr отслеживает набор владельцев , чтобы гарантировать, что:

  • , когда набор владельцев не пуст, delete p не вызывается
  • , когда набор владельцев становится пустым, delete p (или копия D функтора уничтожения).) вызывается немедленно;

Конечно, чтобы определить, когда набор владельцев становится пустым, shared_ptr нужен только счетчик.Абстрактное описание немного проще для понимания.

Возможные методы реализации

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

Чтобы отслеживать всех владельцев, связанный список владельцев является не только очевидным решением, но и простым способом избежать необходимости выделять какие-либопамять для каждого набора владельцев.Проблема в том, что нелегко сделать такой подход эффективно потокобезопасным (все можно сделать потокобезопасным с помощью глобальной блокировки, что противоречит самой идее параллелизма).

В случае многопоточной реализации

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

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

О нескольких возможных реализациях

Сколько раз я читал, что возможны многие реализации для "стандартного" класса?

Кто никогда не слышал эту фантазию о том, что сложный класс может быть реализован как полярные координаты?Это идиотизм, как мы все знаем.комплекс должен использовать декартовы координаты.Если полярные координаты предпочтительны, необходимо создать другой класс.Нет никакого способа, которым полярный комплексный класс будет использоваться для замены обычного комплексного класса.

То же самое для (нестандартного) строкового класса: для строкового класса нет причин.быть внутренним NUL-завершением и не хранить длину как целое число, просто для забавы и неэффективности повторного вызова strlen.

Теперь мы знаем, что проектирование std::string для допуска COW было плохой идеей, котораяпричина необычной семантики аннулирования константных итераторов.

std::vector теперь гарантированно будет непрерывной.

Конец фантазии

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

Программист должен иметь возможность делать переносимые предположения об относительной скорости основных операций.Сложный класс бесполезен для серьезного сокращения чисел, если даже простейшее сложение превращается в кучу трансцендентных вычислений.Если не гарантируется, что у класса строк будет очень быстрое копирование с помощью совместного использования данных, программист должен будет минимизировать количество копий строк.

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

Для многих классов это означает, что существует ровно одна жизнеспособная стратегия реализации, иногда с несколькими степенями свободы (например, размером блока).в std::deque).

5 голосов
/ 11 сентября 2011

Вы правы, ничто в спецификации не требует использования явного "счетчика", и существуют другие возможности.

Например, реализация связанного списка была предложена для реализации boost's shared_ptr;однако в конечном итоге это предложение было отклонено, поскольку в него были включены расходы в других областях (размер, операции копирования и безопасность потоков).

...