Понимание третьего примера Скотта Мейерса для std::weak_ptr

В последнем примере на странице 137 книги Effective Modern C++ показан сценарий структуры данных с объектами A, B и C, соединенными друг с другом через std::shared_ptr следующим образом:

   std::shared_ptr       std::shared_ptr
A ─────────────────▶ B ◀───────────────── C

Для меня это означает, что классы, экземплярами которых являются объекты A и C (как правило, это два несвязанных класса), должны содержать элемент std::shared_ptr<classOfB>.

Затем делается предположение, что нам нужен указатель от B обратно к A, и перечисляются доступные варианты: указатель может быть необработанным, разделяемым или слабым, и последний выбирается как лучший кандидат.

   std::shared_ptr       std::shared_ptr
A ─────────────────▶ B ◀───────────────── C
▲                    │
│    std::weak_ptr   │
└────────────────────┘

Я понимаю слабости (ахахах) первых двух вариантов, но я также вижу, что третий вариант требует, чтобы член A уже управлялся каким-то std::shared_ptr, иначе как std::weak_ptr вообще может указывать на него ?

Однако книга не ссылается на это ограничение/предположение/что-то еще, так что правда либо

  • я не прав
  • Я прав, но это предположение очевидно, по какой-то непонятной мне причине
  • Предположение очевидно именно по той причине, что std::weak_ptr нуждается в уже существующем std::shared_ptr для того же объекта, но немного странно, что это даже не упоминается в начале примера, я думаю.

и я задаю этот вопрос, чтобы понять это.


person Enlico    schedule 19.11.2020    source источник
comment
если A не обрабатывается std::shared_ptr, альтернатив нет...   -  person Jarod42    schedule 19.11.2020
comment
Я думаю, что это предположение справедливо. weak_ptr используется здесь только как средство фиксации shared_ptr петель. Либо вы уже управляли временем жизни A с помощью общего указателя, и в этом случае вы хотели бы использовать слабый указатель, либо вы вообще не рассматривали умные указатели, и в этом случае B не может убедиться, что A жив тем не мение. Альтернатив в любом случае нет.   -  person IS4    schedule 19.11.2020
comment
@IllidanS4supportsМоника, я не думал об этом. Пожалуйста, не стесняйтесь опубликовать это как ответ.   -  person Enlico    schedule 19.11.2020


Ответы (2)


Неблагоприятное последствие конструкции std::shared_ptr заключается в том, что в графе зависимостей, управляемом общими указателями, не может быть циклов. Это означает, что как только A указывает на B через общий указатель, B не может точно так же указывать на A, так как это приведет к утечке памяти (оба объекта останутся в живых).

std::weak_ptr служит своей основной цели в качестве слабой ссылки, но в большинстве случаев он используется только для исправления этой проблемы. Однако, если вы в первую очередь не управляете временем жизни A с помощью общего указателя, B все равно не сможет его отследить, поэтому единственным вариантом является использование необработанного указателя, ссылки (или какого-либо другого экзотического указателя). И наоборот, если вы владеете A через общий указатель, weak_ptr — единственный вариант.

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

person IS4    schedule 19.11.2020

Вы правы, и ваш третий пункт правильный. std::weak_ptr всегда относится к существующему std::shared_ptr. Итак, как только мы приняли решение, что A будет содержать shared_ptr<BClass> и что экземпляры A управляются как shared_ptr<AClass>, мы можем использовать weak_ptr<AClass> для обратной ссылки от B к A.

В типичном случае использования у вас может быть D-класс, который управляет тремя членами shared_ptr<AClass> a;, shared_ptr<BClass> b; и shared_ptr<CClass> c;, а затем некоторая функция-член в D-классе выполняет a->SetB(b);, c->SetB(b);, b->SetA(a);, с функциями-членами SetB, использующими shared_ptr, и SetA, использующими weak_ptr (или преобразование в weak_ptr внутри функции-члена). Как вы правильно заметили, если бы D хранил ссылку на A каким-либо другим способом, например, в виде необработанного указателя AClass* a; или экземпляра AClass a;, то использование weak_ptr было бы просто невозможно.

person Ángel José Riesgo    schedule 19.11.2020
comment
Я сомневался в первом всегда в вашем ответе, потому что на странице 134 Скотт пишет, что _std::weak_ptr обычно создаются из std::shared_ptr. Возможно, под нетипичным он намекает на копирование std::weak_ptr (через копировальный центр). - person Enlico; 19.11.2020