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



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



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



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



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



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

745   3  

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.0 QSharedDataPointer поскольку это небольшое увеличение контроля над тем, когда именно отсоединить после того, как счетчик ссылок падает до нуля, не особенно стоит целого нового объекта.

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

    Ничего не найдено.