Сначала необходимо разработать стратегию владения узлом (или ребром).
Например, в примере с бинарным деревом, который вы размещаете, родитель владеет своими дочерними узлами, но не своим родительским узлом. Такой дизайн гарантирует, что каждый узел в дереве принадлежит ровно одному другому узлу, за исключением корневого узла, из которого вы можете сделать особый случай. Поскольку в этом примере у каждого узла есть только один владелец (его родитель), unique_ptr
можно использовать для моделирования этой связи. Кроме того, в этом примере ссылка от дочернего объекта к родительскому является ссылкой без владения, поэтому ее нельзя смоделировать с помощью интеллектуального указателя-владельца.
В примере с двоичным деревом ваш граф-владелец является ациклическим, направленным, и каждый узел указывает на только один раз (с помощью указателей-владельцев).
В более сложном примере граф может быть ациклическим, направленным, но узлы могут быть указаны несколько раз. Такой граф мог бы использовать shared_ptr
для моделирования ссылок, на которые указывают, поскольку такие ссылки совместно владеют указателем.
Однако надо быть осторожным. Как только ваш график станет циклическим, shared_ptr
больше нельзя будет использовать исключительно. Каждый раз, когда вы устанавливаете цикл владения:
A owns B which owns C which owns A
то вы устроили утечку памяти. Такие циклы можно создавать с помощью shared_ptr
или unique_ptr
. Но на практике циклы, как правило, происходят чаще при использовании shared_ptr
, вероятно, просто потому, что диаграмма владения по своей сути более сложна, чем unique_ptr
.
shared_ptr
содержит вспомогательный класс для разрушения шаблонов циклического владения: weak_ptr
. Это можно использовать для настройки чего-то вроде:
A owns B which owns C which has a (non-owning) weak_ptr to A
Если граф неориентированный, и вы моделируете узлы A и B с помощью:
A points to B and B points to A
то вы сразу устанавливаете цикл, и поэтому оба эти указателя не могут быть указателями-владельцами. В такой ситуации вам придется проектировать, что чем владеет. Возможно, полностью отдельный фрагмент кода может владеть всеми узлами, а все ребра могут быть представлены невладеющими указателями. Возможно, граф можно разделить на набор ациклических направленных ребер (представляющих указатели-владельцы) и всех остальных ребер (указатели, не являющиеся владельцами). Действительно, это именно то, что мы сделали с бинарным деревом — владея дочерними указателями и не владеющими родительскими указателями.
Каким бы ни был ваш дизайн, как только он будет завершен, он приведет вас к тому, являются ли shared_ptr
и/или unique_ptr
подходящими инструментами для реализации вашего дизайна. Если что-то всегда будет принадлежать одному человеку, то unique_ptr
является приемлемым вариантом. Если что-то должно принадлежать нескольким другим объектам, shared_ptr
может быть правильным инструментом (и определенно не unique_ptr
). Если граф владения shared_ptr
содержит циклы, их можно разорвать, заменив некоторые из этих связей на weak_ptr
. Если вам не удастся обнаружить и разорвать циклы в графе владения, циклы приведут к утечкам памяти.
person
Howard Hinnant
schedule
08.12.2014