Какие реализации интеллектуальных указателей на C++ доступны?

сравнения, плюсы, минусы и когда использовать?

Это спин-офф от поток сбора мусора где то, что я думал, был простой ответ сгенерировал много комментариев о некоторых конкретных реализациях смарт-указателя, так что, казалось, стоит начать новый пост.

в конечном счете вопрос в том, каковы различные реализации интеллектуальных указателей в C++ и как они сравниваются? Просто плюсы и минусы или исключения и gotchas к чему-то, что вы могли бы иначе подумать, должно работать.

я опубликовал некоторые реализации, которые я использовал или, по крайней мере, замазал и рассмотрел использование в качестве ответа ниже, и мое понимание их различий и сходств, которые могут быть не на 100% точными, поэтому не стесняйтесь проверять факты или исправлять меня по мере необходимости.

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

3 ответов


C++03

std::auto_ptr - возможно, один из оригиналов страдал синдромом первого проекта, предоставляя ограниченные возможности для сбора мусора. Первым недостатком является то, что он называет delete при уничтожении делает их неприемлемыми для хранения массива выделенных объектов (new[]). Он принимает владение указателем, поэтому два автоматических указателя не должны содержать один и тот же объект. Назначение передаст владение и переустановит правосторонним значением автоматический указатель на нулевой указатель. Что приводит, возможно, к худшему недостатку; они не могут использоваться в контейнерах STL из-за вышеупомянутой невозможности копирования. Последний удар по любому варианту использования-они должны быть устаревшими в следующем стандарте C++.

std::auto_ptr_ref - Это не умный указатель, это на самом деле деталь дизайна, используемая в сочетании с std::auto_ptr разрешить копирование и назначение в определенных ситуациях. Специфически его можно использовать к преобразование не-const std::auto_ptr до lvalue используя трюк Колвина-Гиббонса, также известный как переместить конструктор переход права собственности.

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


C++11

std::unique_ptr - это наш друг, который будет заменять std::auto_ptr Это будет очень похоже, за исключением ключевых улучшений для исправления слабостей std::auto_ptr Как работать с массивами, lvalue защита через конструктор частной копии, используемый с контейнерами и алгоритмами STL и т. д. Поскольку это производительность и объем памяти ограничен, это идеальный кандидат на замену или, возможно, более точно описанный как владелец, необработанные указатели. Как следует из "уникального", существует только один владелец указателя, как и предыдущий std::auto_ptr.

std::shared_ptr - я считаю, что это основано на TR1 и boost::shared_ptr но улучшено, чтобы включить арифметику сглаживания и указателя. Короче говоря, он обертывает интеллектуальный указатель с подсчетом ссылок вокруг динамически выделенного объекта. Как "общий" подразумевает, что указатель может принадлежать более чем одной общий указатель когда последняя ссылка последнего общего указателя выходит за пределы области, объект будет удален соответствующим образом. Они также потокобезопасны и в большинстве случаев могут обрабатывать неполные типы. std::make_shared может использоваться для эффективного построения std::shared_ptr С одним распределением кучи с использованием распределителя по умолчанию.

std::weak_ptr - аналогично на основе TR1 и boost::weak_ptr. Это ссылка на объект, принадлежащий std::shared_ptr и поэтому не предотвратит удаление объекта, если std::shared_ptr счетчик ссылок падает до нуля. Чтобы получить доступ к необработанному указателю, вам сначала нужно получить доступ к std::shared_ptr по телефону lock который вернет пустой std::shared_ptr если срок действия принадлежащего указателя истек и он уже уничтожен. Это в первую очередь полезно, чтобы избежать неопределенного количества ссылок при использовании нескольких интеллектуальных указателей.


Boost

boost::shared_ptr - наверное, проще всего использовать в самых разных сценариях (STL, PIMPL, RAII и т. д.) Это общий ссылочный подсчитанный смарт-указатель. Я слышал несколько жалоб на производительность и накладные расходы в некоторых ситуациях, но я, должно быть, проигнорировал их, потому что не могу вспомнить, что это был за аргумент. По-видимому, он был достаточно популярен, чтобы стать ожидающим стандартным объектом C++, и никакие недостатки по сравнению с нормой в отношении интеллектуальных указателей не приходят на ум.

boost::weak_ptr - как и предыдущее описание из std::weak_ptr, основываясь на этой реализации, это позволяет не владеть ссылкой на boost::shared_ptr. Ты не удивительно называешь lock() для доступа к "сильному" общему указателю и должен проверить, что он действителен, поскольку он уже мог быть уничтожен. Просто убедитесь, что не хранить общий указатель возвращается и пусть он выходит из области, как только вы закончите с ним в противном случае вы вернетесь к проблеме циклической ссылки, где ваши отсчеты ссылок будут висеть, а объекты не будут разрушенный.

boost::scoped_ptr - это простой класс smart pointer с небольшими накладными расходами, вероятно, предназначенный для лучшей альтернативы boost::shared_ptr когда использовать. Это сопоставимо с std::auto_ptr особенно в том, что его нельзя безопасно использовать как элемент контейнера STL или с несколькими указателями на один и тот же объект.

boost::intrusive_ptr - я никогда не использовал это, но из моего понимания он предназначен для использования при создании вашего собственные классы, совместимые с smart pointer. Вам нужно реализовать подсчет ссылок самостоятельно, вам также нужно будет реализовать несколько методов, если вы хотите, чтобы ваш класс был универсальным, кроме того, вам придется реализовать собственную безопасность потоков. С положительной стороны это, вероятно, дает вам самый пользовательский способ выбора и выбора именно того, сколько или как мало "умности" вы хотите. intrusive_ptr обычно более эффективен, чем shared_ptr поскольку это позволяет иметь одно распределение кучи на объект. (спасибо Арвид)

boost::shared_array - это boost::shared_ptr для массивов. В основном new [], operator[] и конечно delete [] запекаются. Это можно использовать в контейнерах STL и, насколько я знаю, делает все boost:shared_ptr делает, хотя вы не можете использовать boost::weak_ptr С этими. Однако вы можете альтернативно использовать boost::shared_ptr<std::vector<>> для подобной функциональности и восстановить способность использовать boost::weak_ptr для ссылок.

boost::scoped_array - Это boost::scoped_ptr для массивов. Как с boost::shared_array вся необходимая доброта массива испечена внутри. Этот не копируется и поэтому не может использоваться в контейнерах STL. Я нашел почти везде, где вы хотите использовать это, вы, вероятно, могли бы просто использовать std::vector. Я никогда не определял, что на самом деле быстрее или имеет меньше накладных расходов, но этот массив области кажется гораздо менее вовлеченным, чем вектор STL. Если вы хотите сохранить выделение в стеке, рассмотрите boost::array вместо.


Qt

QPointer - введено в Qt 4.0 это "слабый" умный указатель, который работает только с QObject и производные классы, которые в Qt framework являются почти все так, что это не ограничение. Однако есть ограничения, а именно, что он не предоставляет" сильный " указатель, и хотя вы можете проверить, действителен ли базовый объект с isNull() вы можете найти свой объект уничтожается сразу после прохождения этой проверки, особенно в многопоточных средах. Я считаю, что люди Qt считают это устаревшим.

QSharedDataPointer - это "сильный" умный указатель, потенциально сопоставимый с boost::intrusive_ptr хотя он имеет некоторую встроенную потокобезопасность, но требует включения методов подсчета ссылок (ref и deref) что вы можете сделать, подклассы QSharedData. Как и в большинстве Qt, объекты лучше всего использовать через ample наследование и подклассы все, кажется, является предполагаемым дизайном.

QExplicitlySharedDataPointer - очень похоже на QSharedDataPointer разве это не неявно вызов detach(). Я бы назвал эту версию 2.0 QSharedDataPointer поскольку это небольшое увеличение контроля относительно того, когда именно отсоединяться после того, как счетчик ссылок падает до нуля, не особенно стоит целого нового объекта.

QSharedPointer - атомарный отсчет ссылки, потокобезопасный, sharable указатель, таможня удаляет (поддержка массива), звучит как все, что должен быть умный указатель. Это то, что я в первую очередь использую как умный указатель в Qt, и я нахожу его сопоставимым с boost:shared_ptr хотя, вероятно, значительно больше накладных расходов, как и многие объекты в Qt.

QWeakPointer - вы чувствуете повторяющуюся картину? Так же, как std::weak_ptr и boost::weak_ptr это используется в сочетании с QSharedPointer когда вам нужны ссылки между двумя интеллектуальными указателями, которые в противном случае заставили бы ваши объекты никогда не удаляться.

QScopedPointer - это имя также должно выглядеть знакомым и на самом деле было основано на boost::scoped_ptr в отличие от версий Qt общих и слабых указателей. Он функционирует для обеспечения одного владельца смарт-указатель без накладных расходов QSharedPointer что делает его более подходящим для совместимости, безопасного кода исключения и всех вещей, которые вы можете использовать std::auto_ptr или boost::scoped_ptr for.


есть еще Локи который реализует интеллектуальные указатели на основе политики.

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


в дополнение к данным, есть также некоторые ориентированные на безопасность:

SaferCPlusPlus

mse::TRefCountingPointer является ссылкой подсчета смарт-указатель, как std::shared_ptr. Разница в том, что mse::TRefCountingPointer безопаснее, меньше и быстрее, но не имеет механизма безопасности резьбы. И он поставляется в версиях" not null "и" fixed " (non-retargetable), которые можно с уверенностью предположить, что всегда указывают на достоверно выделенный объект. Так что в основном, если ваш целевой объект разделяется между асинхронными потоками, а затем использует std::shared_ptr, иначе mse::TRefCountingPointer более оптимальным.

mse::TScopeOwnerPointer похож на boost::scoped_ptr, но работает в сочетании с mse::TScopeFixedPointer в отношениях" сильный-слабый " указатель типа std::shared_ptr и std::weak_ptr.

mse::TScopeFixedPointer указывает на объекты, которые выделены в стеке или чей указатель "владелец" выделен в стеке. Он (намеренно) ограничен в своей функциональности для повышения безопасности во время компиляции без стоимость выполнения. Смысл указателей "scope" заключается, по сути, в определении набора обстоятельств, которые достаточно просты и детерминированы, чтобы не было необходимости в механизмах безопасности (во время выполнения).

mse::TRegisteredPointer ведет себя как исходный указатель, за исключением того, что его значение автоматически устанавливается в значение null_ptr, когда объект уничтожается. Его можно использовать в качестве общей замены необработанных указателей в большинстве ситуаций. Как необработанный указатель, он не имеет никакой внутренней безопасности потока. Но взамен он не имеет проблем с таргетингом объектов, выделенных в стеке (и получением соответствующего преимущества производительности). Если включены проверки во время выполнения, этот указатель не имеет доступа к недопустимой памяти. Потому что mse::TRegisteredPointer имеет то же поведение, что и необработанный указатель при указании на допустимые объекты, он может быть "отключен" (автоматически заменен соответствующим необработанным указателем) директивой времени компиляции, что позволяет использовать его для улавливания ошибок в режимах отладки / тестирования / бета-тестирования, не требуя никаких накладных расходов в режиме выпуска.

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