Какой указатель я использую, когда?



хорошо, так что в последний раз, когда я писал C++ для жизни,std::auto_ptr все СТД Либ был доступен, и boost::shared_ptr был весь гнев. Я никогда не заглядывал в другие типы интеллектуальных указателей, предоставляемые boost. Я понимаю, что C++11 теперь предоставляет некоторые из типов boost, но не все из них.



Итак, у кого-то есть простой алгоритм, чтобы определить, когда использовать какой умный указатель? Предпочтительно включая советы относительно тупых указателей (необработанные указатели, такие как T*) и остальные интеллектуальные указатели boost. (Что-то вроде этого было бы здорово).

635   4  

4 ответов:

долевой собственности:
Элемент shared_ptr и weak_ptr принятый стандарт в значительной степени совпадает с их увеличить коллегами. Используйте их, когда вам нужно поделиться ресурсом и не знаете, какой из них будет последним, чтобы быть живым. Используйте weak_ptr наблюдать за общим ресурсом, не влияя на его срок службы, не нарушать циклы. Циклы с shared_ptr обычно не должно происходить-два ресурса не могут владеть друг другом.

обратите внимание, что повысить дополнительно предлагает shared_array, что может быть подходящей альтернативой для shared_ptr<std::vector<T> const>.

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

уникальная собственности:
Повышение также имеет scoped_ptr, который не копируется и для которого вы не могу указать делетер. std::unique_ptr и boost::scoped_ptr на стероидах и должно быть вашим выбор по умолчанию, если вам нужен умный указатель. Это позволяет указать делетер в его аргументах шаблона и является движимое в отличие от boost::scoped_ptr. Он также полностью используется в контейнерах STL, если вы не используете операции, которые нуждаются в копируемых типах (очевидно).

еще раз обратите внимание, что Boost имеет версию массива:scoped_array, который стандарт унифицировал мимо требуя std::unique_ptr<T[]> частичная специализация, которая будет delete[] указатель вместо deleteing его (с default_deleter). std::unique_ptr<T[]> предлагает operator[] вместо operator* и operator->.

обратите внимание, что std::auto_ptr все еще в стандарте, но это устаревший. §D.10 [depr.auto.ptr]

шаблон класс auto_ptr устарела. [ Примечание: шаблон класс unique_ptr (20.7.1) обеспечивает лучшее решение. -конец Примечание]

нет собственности:
Используйте тупые указатели (необработанные указатели) или ссылки для не владеющие ссылки ресурсы, и когда вы знаете, что ресурс переживет объект / область ссылки. Предпочитайте ссылки и используйте необработанные указатели, когда вам нужна либо возможность обнуления, либо перенастройка.

если вы хотите не владеющий ссылкой на ресурс, но вы не знаете, если ресурс переживет объект, который ссылается на него, упакуйте ресурс в shared_ptr и использовать weak_ptr - вы можете проверить, если родитель shared_ptr живет с lock, который вернет a shared_ptr это ненулевое значение, если ресурс все еще существует. Если вы хотите проверить, мертв ли ресурс, используйте expired. Эти два могут звучать похоже, но очень разные перед лицом параллельного выполнения, как expired только гарантирует его возвращаемое значение для этого одного оператора. Казалось бы, невинный тест как

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();

- это потенциальное состояние гонки.

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

обратите внимание, что владение в программной системе является отдельно от собственности, как мы думаем об этом за пределами программного обеспечения. Например, человек может "владеть" своим домом, но это не обязательно означает, что Person объект имеет контроль над временем жизни a House "объект". Объединение этих концепций реального мира с концепциями программного обеспечения-это верный способ запрограммировать себя в дыру.


если у вас есть единоличное право собственности на объект, использовать std::unique_ptr<T>.

если у вас есть общее право собственности на объект...
- Если нет циклов в собственности, используйте std::shared_ptr<T>.
- Если есть циклы, определите "направление" и используйте std::shared_ptr<T> в одну сторону и std::weak_ptr<T> в другой.

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

если объект принадлежит вам (или иным образом имеет гарантированное существование), Используйте ссылки T&.


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

затраты:

  • если у вас есть пользовательский делетер (например, вы используете пулы распределения), то это приведет к накладным расходам на указатель, которые можно легко избежать путем ручного удаления.
  • std::shared_ptr имеет накладные расходы на увеличение количества ссылок при копировании, а также уменьшение при уничтожении с последующей проверкой 0-count с удалением удерживаемого объекта. В зависимости от реализации это может привести к раздуванию кода и вызвать проблемы с производительностью.
  • время компиляции. Как и во всех шаблонах, интеллектуальные указатели негативно влияют на время компиляции.

примеры:

struct BinaryTree
{
    Tree* m_parent;
    std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};

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

struct ListNode
{
    std::shared_ptr<ListNode> m_next;
    std::weak_ptr<ListNode> m_prev;
};

здесь узел списка владеет своим следующим и предыдущим списками, поэтому мы определяем направление и используем shared_ptr на следующий и weak_ptr для prev, чтобы разорвать цикл.

использовать unique_ptr<T> все время, кроме тех случаев, когда вам нужен подсчет ссылок, в этом случае используйте shared_ptr<T> (и в очень редких случаях, weak_ptr<T> для предотвращения опорных циклов). Почти в каждом случае передача уникального владения просто прекрасна.

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

массив указателей: 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 немного менее удобный, хотя, возможно, общее преимущество и отсутствие зависимости от повышения делает unique_ptr и shared_ptr снова победители.

указатели с областью действия: сделаны неуместными unique_ptr, так как auto_ptr.

там действительно больше ничего нет. В C++03 без семантики перемещения эта ситуация была очень сложной, но в C++11 совет очень прост.

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

случаи, когда использовать unique_ptr:

  • методы фабрики
  • члены, которые являются указателями (pimpl включены)
  • хранение указателей в контейнерах stl (чтобы избежать перемещений)
  • использование больших локальных динамических объектов

случаи, когда использовать shared_ptr:

  • совместное использование объектов в потоках
  • общий доступ к объектам в целом

случаи, когда использовать weak_ptr:

  • большая карта, которая действует как общая ссылка (ex. карта всех открытых сокетов)

не стесняйтесь редактировать и добавлять больше

Comments

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