Какие реализации интеллектуального указателя C++ доступны?
сравнения, плюсы, минусы и когда использовать?
Это спин-офф от поток сборки мусора где то, что я думал, было простым ответом, породило много комментариев о некоторых конкретных реализациях интеллектуальных указателей, поэтому казалось, что стоит начать новый пост.
в конечном счете вопрос в том, каковы различные реализации интеллектуальных указателей в C++ и как они сравниваются? Просто плюсы и минусы или исключения и gotchas к чему-то, что вы могли бы иначе думать, должно работать.
я опубликовал некоторые реализации, которые я использовал или, по крайней мере, замалчивал и рассматривал в качестве ответа ниже, и мое понимание их различий и сходств, которые могут быть не на 100% точными, поэтому не стесняйтесь проверять факты или исправлять меня по мере необходимости.
цель состоит в том, чтобы узнать о некоторых новых объектах и библиотеках или исправить мое использование и понимание существующих реализаций, уже широко используемых и в конечном итоге с достойной ссылкой для других.
3 ответов:
C++03
std::auto_ptr- возможно, один из оригиналов он страдал от синдрома первого проекта, предоставляя только ограниченные средства для сбора мусора. Первый недостаток в том, что он называетdeleteпри уничтожении делая их неприемлемыми для удержания массива выделенных объектов (new[]). Он берет на себя ответственность за указатель, поэтому два автоматических указателя не должны содержать один и тот же объект. Назначение перенесет владение и переустановит rvalue авто указатель на нулевой указатель. Что приводит, возможно, к худшему недостатку; они не могут использоваться в контейнерах STL из-за вышеупомянутой невозможности копирования. Последний удар по любому варианту использования-они должны быть устаревшими в следующем стандарте C++.
std::auto_ptr_ref- Это не умный указатель, это на самом деле деталь дизайна, используемая в сочетании сstd::auto_ptrразрешить копирование и назначение в определенных ситуациях. В частности, она может использоваться для перевести неконстантный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- это простой класс интеллектуального указателя с небольшими накладными расходами, вероятно, предназначенный для лучшей альтернативыboost::shared_ptrкогда использовать. Это сравнимо сstd::auto_ptrособенно в том, что он не может быть безопасно использован в качестве элемента контейнера STL или с несколькими указателями на один и тот же объект.
boost::intrusive_ptr- я никогда не использовал это, но из моего понимания он предназначен для использования при создании вашего собственные классы, совместимые с интеллектуальным указателем. Вам нужно реализовать подсчет ссылок самостоятельно, вам также нужно будет реализовать несколько методов, если вы хотите, чтобы ваш класс был универсальным, кроме того, вам нужно будет реализовать свою собственную потокобезопасность. С положительной стороны это, вероятно, дает вам самый пользовательский способ выбора и выбора именно того, сколько или как мало "умности" вы хотите.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 являются почти все так, что это не ограничение. Однако есть ограничения, а именно, что он не предоставляет" сильный " указатель, и хотя вы можете проверить, является ли базовый объект допустимым сisNull()вы можете найти свой объект уничтожается сразу после прохождения этой проверки, особенно в многопоточных средах. Я считаю, что люди считают это устаревшим.
QSharedDataPointer- это "сильный" умный указатель, потенциально сравнимый сboost::intrusive_ptrхотя он имеет некоторые встроенные в потокобезопасности, но это требует от вас, чтобы включить методы подсчета ссылок (refиderef), которые вы можете сделать через наследованиеQSharedData. Как и в большинстве Qt, объекты лучше всего использовать через достаточно наследование и подклассы все, кажется, предполагаемый дизайн.
QExplicitlySharedDataPointer- очень похоже наQSharedDataPointerза исключением того, что он неявно вызываетdetach(). Я бы назвал эту версию 2.0QSharedDataPointerпоскольку это небольшое увеличение контроля над тем, когда именно отсоединить после того, как счетчик ссылок падает до нуля, не особенно стоит целого нового объекта.
QSharedPointer- атомарный подсчет ссылок, потокобезопасный, разделяемый указатель, пользовательский удаляет (поддержка массива), звучит как все, что должен быть умный указатель. Это то, что я в первую очередь использую в качестве интеллектуального указателя в 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для.
есть еще Локи который реализует интеллектуальные указатели на основе политик.
другие ссылки на интеллектуальные указатели на основе политик, решающие проблему плохой поддержки оптимизации пустой базы наряду с множественным наследованием многими компиляторами:
в дополнение к тем, которые даны, есть некоторые ориентированные на безопасность тоже:
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имеет то же поведение, что и необработанный указатель при указании на допустимые объекты, он может быть "отключен" (автоматически заменен соответствующим необработанным указателем) с директивой времени компиляции, что позволяет использовать его для обнаружения ошибок в режимах отладки / тестирования / бета-тестирования, не неся никаких накладных расходов в режиме выпуска.здесь это статья, описывающая, почему и как их использовать. (Заметьте, бесстыдный штекер.)
Comments