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

#c #c 11 #shared-ptr #weak-ptr #effective-c

#c #c 11 #общий-ptr #слабый ptr #эффективный-c

Вопрос:

Последний пример на странице 137 Эффективного современного C рисует сценарий структуры данных с объектами A , B , и C в нем, связанными друг std::shared_ptr с другом через следующим образом:

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

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

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

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

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

Однако в книге не упоминается это «ограничение» / предположение / что угодно, поэтому истина либо

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

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

Комментарии:

1. если A не обрабатывается std::shared_ptr , альтернатив нет…

2. Я думаю, что это предположение справедливо. weak_ptr используется здесь только как средство исправления shared_ptr циклов. Либо вы уже управляли временем жизни A с помощью общего указателя, и в этом случае вы хотели бы использовать слабый указатель, либо вы вообще не рассматривали интеллектуальные указатели, и в этом случае у B нет способа убедиться, что A все равно жив. В любом случае альтернатив нет.

3. @IllidanS4supportsMonica, я не думал об этом. Пожалуйста, не стесняйтесь публиковать его в качестве ответа.

Ответ №1:

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

В типичном случае использования у вас может быть общий класс, который управляет тремя членами shared_ptr<AClass> a; , shared_ptr<BClass> b; и shared_ptr<CClass> c; , а затем какая-либо функция-член в общем классе выполняет 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 было бы просто невозможно.

Комментарии:

1. Я сомневался в первом всегда в вашем ответе, потому что на странице 134 Скотт пишет, что _s std::weak_ptr обычно создаются из std::shared_ptr s . Возможно, под нетипичным он намекает на копирование a std::weak_ptr (через ctor копирования).

Ответ №2:

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

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

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