Умные указатели + циклы + "->"

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

Мой вопрос: означает ли это, что у меня плохой дизайн?

Что делать, если я хочу реализовать график? Могу ли я использовать интеллектуальные указатели? На графиках есть циклы, но со слабым_ptr я не могу использовать "- > ". Что я могу сделать?

Я прочитал некоторые статьи, ссылки и темы в StackOverflow, но похоже, что я до сих пор не получаю умных указателей. Действительно, почему не существует какой-то вариант weak_ptr с "- > "?

Ответ 1

Подходите к этому с концептуальной стороны, а не с ее реализации. Умные указатели представляют собой собственность. И существование умных указателей не делает недействительной роль необработанных указателей как не владеющих наблюдателями.

Есть ли у каждого объекта один, четко определенный владелец (например, граф владеет всеми его вершинами и ребрами)? Если это так, используйте std::unique_ptr для хранения вершин и ребер в графе и используйте необработанные указатели внутри вершин и ребер, чтобы ссылаться друг на друга.

Разрешено ли совместное владение (например, вершина существует только в том случае, если к ней подключено хотя бы одно ребро)? Если это так, используйте std::shared_ptr для представления этого права собственности, опять же с необработанными указателями для несоблюдения наблюдателей. Если вам требуется взаимное владение (т.е. циклы собственности), где "вершина существует только до тех пор, пока к ней обращается ребро, а край существует только до тех пор, пока вершина ссылается на нее", тогда 1. дважды проверьте, что такой проект правильный и поддерживаемый, и 2. если это так, используйте std::weak_ptr где-нибудь в цикле, чтобы разбить контур владения. Вы всегда можете lock() a weak_ptr получить shared_ptr.

В вашем конкретном графическом сценарии я считаю, что "все, что принадлежит графику", было бы наиболее логичной схемой собственности; но это зависит от идиосинкразий вашей задачи.

Ответ 2

Вы можете использовать weak_ptr где-то в цикле; вам просто нужно продвигать weak_ptr до shared_ptr, прежде чем вы сможете разыменовать их. Вы можете сделать это, вызвав weak_ptr::lock() или просто передав конструктор weak_ptr to shared_ptr (но будьте осторожны: это вызовет исключение bad_weak_ptr, если объект, на который указывает weak_ptr, был уничтожен.

Если вы действительно не можете этого сделать (например, если все объекты, участвующие в цикле, имеют один и тот же тип, что, вероятно, имеет место в примере вашего графа), другой вариант заключается в том, чтобы поместить функцию release где-то в цепочке, которая заставляет объект, о котором идет речь, установить для всех своих shared_ptr значение null.

Ответ 3

Означает ли это, что у меня плохой дизайн?

Да, но это отправная точка.

Давайте рассмотрим некоторые из доступных интеллектуальных указателей.

unique_ptr - существует один владелец, который несет ответственность за удаление объекта.

shared_ptr - существует много (или потенциально много) владельцев, а последнее должно распоряжаться объектом

weak_ptr - многие владельцы могут существовать, но это не один из них, слабый указатель может покинуть объект, на который указывает объект, если объект, на который указывает, удаляется слабый указатель, будет нулевым (то есть метод блокировки вернет null shared_ptr)

observer_ptr (n3840) - Еще не является частью стандарта, поэтому вместо этого могут использоваться указатели стиля C (T *), Они очень похожи на weak_ptr, но ответственность программистов заключается в том, чтобы убедиться, что все наблюдатели не разыменованы после того, как объект, на который указывает, удаляется.

Решение состоит в том, чтобы разделить проект на объект, который будет владеть всеми кусками и кусками (узлами цикла). Собственный объект может использовать shared_ptr или unique_ptr для автоматического управления временем жизни узлов. Сами узлы могут ссылаться друг на друга с помощью weak_ptr, observer_ptr или ссылки (Node &)