Итак, в прошлый раз, когда я писал на С ++, std::auto_ptr
все, что было доступно в std lib, boost::shared_ptr
было в ярости. Я действительно никогда не смотрел на другие типы улучшенных умных указателей. Я понимаю, что C ++ 11 теперь предоставляет некоторые из типов boost, но не все.
Так есть ли у кого-нибудь простой алгоритм, чтобы определить, когда использовать какой умный указатель? Желательно включать советы, касающиеся тупых указателей (например, необработанных указателей T*
) и остальных интеллектуальных указателей повышения. (Что - то вроде этого было бы здорово).
Ответы:
Общая собственность: и стандарт принят в значительной степени так же , как их буста аналоги . Используйте их, когда вам нужно поделиться ресурсом, и не знаете, какой из них будет последним живым. Используйте для наблюдения общего ресурса, не влияя на его время жизни, чтобы не прерывать циклы. Циклы с обычно не должны происходить - два ресурса не могут владеть друг другом.
shared_ptr
weak_ptr
weak_ptr
shared_ptr
Обратите внимание, что Boost дополнительно предлагает
shared_array
, что может быть подходящей альтернативойshared_ptr<std::vector<T> const>
.Далее, Повышение предложение
intrusive_ptr
, которые являются легким решением , если ваши предложения ссылки на ресурс подсчет управления уже и вы хотите принять его к принципу RAII. Этот не был принят стандартом.Уникальное владение:
Boost также имеет
scoped_ptr
, который не копируется и для которого вы не можете указать удалитель.std::unique_ptr
этоboost::scoped_ptr
на стероидах и должен быть ваш выбор по умолчанию , если вам нужен смарт - указатель . Это позволяет вам указывать удалитель в аргументах шаблона и является подвижным , в отличие отboost::scoped_ptr
. Он также полностью применим в контейнерах STL, если вы не используете операции, для которых нужны копируемые типы (очевидно).Еще раз отметим, что Boost имеет версию массива:
scoped_array
стандарт унифицирован, требуяstd::unique_ptr<T[]>
частичной специализации, которая будетdelete[]
указателем вместоdelete
него (сdefault_delete
r).std::unique_ptr<T[]>
также предлагаетoperator[]
вместоoperator*
иoperator->
.Обратите внимание, что
std::auto_ptr
все еще в стандарте, но это устарело .§D.10 [depr.auto.ptr]
Без владения:
используйте тупые указатели (необработанные указатели) или ссылки для не владеющих ссылками на ресурсы, а также когда вы знаете, что ресурс переживет ссылающийся объект / область. Предпочитайте ссылки и используйте необработанные указатели, когда вам нужно обнулять или сбрасывать.
Если вы хотите не принадлежащую ссылку на ресурс, но не знаете, переживет ли ресурс объект, который на него ссылается, упакуйте ресурс в
shared_ptr
и используйтеweak_ptr
- вы можете проверить,shared_ptr
жив ли родительский объектlock
, что вернутьshared_ptr
ненулевое значение, если ресурс все еще существует. Если хотите проверить, мертв ли ресурс, используйтеexpired
. Оба могут показаться похожими, но сильно отличаются друг от друга в условиях одновременного выполнения, так какexpired
гарантируют только возвращаемое значение для этого единственного оператора. Казалось бы, невинный тест, какэто потенциальное состояние гонки.
источник
shared_ptr
указатель a, а не принадлежащий указатель - aweak_ptr
...shared_array<T>
альтернативаshared_ptr<T[]>
неshared_ptr<vector<T>>
: она не может расти.Решение о том, какой умный указатель использовать, является вопросом владения . Когда дело доходит до управления ресурсами, объект A владеет объектом B, если он контролирует время жизни объекта B. Например, переменные-члены принадлежат их соответствующим объектам, потому что время жизни переменных-членов связано с временем жизни объекта. Вы выбираете умные указатели в зависимости от того, как принадлежит объект.
Обратите внимание, что владение программной системой отделено от владения, как мы думаем, вне программного обеспечения. Например, человек может «владеть» своим домом, но это не обязательно означает, что
Person
объект имеет контроль над временем жизниHouse
объекта. Сопоставление этих концепций реального мира с концепциями программного обеспечения - верный способ запрограммировать себя в дыру.Если у вас есть единоличное владение объектом, используйте
std::unique_ptr<T>
.Если у вас есть совместное владение объектом ...
- Если в собственности нет циклов, используйте
std::shared_ptr<T>
.- Если есть циклы, определите «направление» и используйте
std::shared_ptr<T>
в одном направлении иstd::weak_ptr<T>
в другом.Если объект принадлежит вам, но существует вероятность отсутствия владельца, используйте обычные указатели
T*
(например, родительские указатели).Если объект принадлежит вам (или иным образом имеет гарантированное существование), используйте ссылки
T&
.Предостережение: знайте о стоимости умных указателей. В средах с ограниченной памятью или производительностью может быть полезно просто использовать обычные указатели с более ручной схемой управления памятью.
Цены:
std::shared_ptr
имеет накладные расходы на увеличение счетчика ссылок на копию, плюс уменьшение на уничтожение, за которым следует проверка 0 счетчика с удалением удерживаемого объекта. В зависимости от реализации, это может раздуть ваш код и вызвать проблемы с производительностью.Примеры:
Двоичное дерево не владеет своим родителем, но существование дерева подразумевает существование его родителя (или
nullptr
для корня), поэтому используется обычный указатель. Бинарное дерево (с семантикой значения) имеет исключительное право собственности на своих потомков, так что это такstd::unique_ptr
.Здесь узлу списка принадлежат его следующий и предыдущий списки, поэтому мы определяем направление и используем
shared_ptr
для следующего иweak_ptr
для предыдущего прерывания цикла.источник
shared_ptr<BinaryTree>
для детей иweak_ptr<BinaryTree>
для родительских отношений.Используйте
unique_ptr<T>
все время, кроме случаев, когда вам необходим подсчет ссылок, в этом случае используйтеshared_ptr<T>
(и в очень редких случаях,weak_ptr<T>
чтобы предотвратить циклы ссылок). Почти в каждом случае передаваемое уникальное право собственности просто прекрасно.Необработанные указатели: хорошо, только если вам нужны ковариантные возвраты, не имеющие указания, которые могут произойти. В противном случае они не очень полезны.
Указатели массива:
unique_ptr
имеет специализацию, дляT[]
которой автоматически вызываетсяdelete[]
результат, так что вы можете безопасно сделать,unique_ptr<int[]> p(new int[42]);
например.shared_ptr
вам все еще нужен пользовательский удалитель, но вам не понадобится специализированный общий или уникальный указатель массива. Конечно, такие вещи, как правило, лучше всего заменить вstd::vector
любом случае. К сожалениюshared_ptr
, не предоставляет функцию доступа к массиву, поэтому вам все равно придется вручную вызыватьget()
, ноunique_ptr<T[]>
предоставляетoperator[]
вместоoperator*
иoperator->
. В любом случае, вы должны проверить себя самостоятельно. Это делаетshared_ptr
немного менее удобным для пользователя, хотя, возможно, общее преимущество и отсутствие зависимости Boost делаетunique_ptr
иshared_ptr
победителей снова.Указатели на область действия: сделаны неактуальными
unique_ptr
, просто такauto_ptr
.Там действительно больше ничего нет. В C ++ 03 без семантики перемещения эта ситуация была очень сложной, но в C ++ 11 совет очень прост.
Есть все еще использование для других умных указателей, таких как
intrusive_ptr
илиinterprocess_ptr
. Тем не менее, они очень нишевые и совершенно ненужные в общем случае.источник
std::unique_ptr<T[]>
обеспечиваетoperator[]
вместоoperator*
иoperator->
. Это правда, что вы все еще должны сделать обязательную проверку, хотя.Случаи, когда использовать
unique_ptr
:Случаи, когда использовать
shared_ptr
:Случаи, когда использовать
weak_ptr
:Не стесняйтесь редактировать и добавлять больше
источник