#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 . Возможно, под нетипичным он намекает на копирование astd::weak_ptr
(через ctor копирования).
Ответ №2:
Печальным следствием разработки std::shared_ptr
является то, что в графе зависимостей, управляемом общими указателями, не может быть циклов. Это означает, что, как только A указывает на B с помощью общего указателя, B не может указывать таким же образом на A, поскольку это приведет к утечке памяти (оба объекта будут оставаться в живых).
std::weak_ptr
служит своей основной цели в качестве слабой ссылки, но в большинстве случаев он используется только как исправление этой проблемы. Однако, если вы не управляете временем жизни A с помощью общего указателя в первую очередь, B все равно не сможет его отслеживать, поэтому использование необработанного указателя, ссылки (или какого-либо другого экзотического указателя) является единственным вариантом. И наоборот, если у вас есть через общий указатель, weak_ptr
это единственный вариант.
В обоих случаях выбор полностью зависит от вашего предыдущего решения об управлении A, что вам пришлось сделать здесь (возможно, через объект, который ссылается как на A, так и на C).