Проанализируйте проблему утечки памяти в следующем C++‑примере и предложите исправление, объяснив различие между shared_ptr и weak_ptr: struct A { shared_ptr b; }; struct B { shared_ptr a; }; void f() { auto a = make_shared(); auto b = make_shared(); a->b = b; b->a = a; }

2 Окт в 13:42
8 +2
0
Ответы
1

Проблема в вашем примере — циклическая ссылка через shared_ptr: A держит shared_ptr на B, а B — shared_ptr на A. shared_ptr реализует подсчёт сильных (owning) ссылок; цикл приводит к тому, что счётчики никогда не станут нулём, объекты не уничтожаются и память «утекает».

Почему так происходит (коротко):

Для каждого объекта есть control block с двумя счётчиками: strong (shared_ptr) и weak (weak_ptr).shared_ptr увеличивает strong; объект уничтожается только когда strong == 0.Если A->b и B->a — shared_ptr, то у каждого есть по крайней мере по 1 strong, strong никогда не станет 0 → деструкторы не вызовутся.

Как исправить

Разорвать цикл, чтобы одна из ссылок была «невладеющей». Для этого используйте std::weak_ptr вместо std::shared_ptr в одной из сторон (обычно в «обратной» или наблюдающей ссылке).

Пример исправления:

include include

struct B;
struct A {
std::shared_ptr b;
~A(){ std::cout << "A destroyed\n"; }
};
struct B {
std::weak_ptr a; // weak_ptr — не владеет A, не увеличивает strong-счётчик
~B(){ std::cout << "B destroyed\n"; }
};

void f() {
auto a = std::make_shared();
auto b = std::make_shared();
a->b = b;
b->a = a; // присваиваем weak_ptr
} // при выходе из f оба объекта будут уничтожены

Если нужно использовать A из B, безопасно получить shared_ptr через lock():

if (auto as = b->a.lock()) {
// as — std::shared_ptr, объект ещё жив
} else {
// объект A уже уничтожен
}

Дополнительные варианты решения

Использовать raw pointer или ссылку для «обсервающей» связи (но тогда надо гарантировать корректность жизни объекта).Явно разрушать цикл (reset()) перед выходом.Пересмотреть владение и использовать std::unique_ptr, если владение уникально.

Коротко о различии shared_ptr и weak_ptr

std::shared_ptr — умный указатель, владеющий объектом; увеличивает strong-счётчик; объект удаляется, когда последний shared_ptr уничтожается.std::weak_ptr — «слабая», невладеющая ссылка; не увеличивает strong-счётчик; позволяет проверять, жив ли объект, и при необходимости временно получить std::shared_ptr через lock(). weak_ptr участвует в weak-счётчике control block и препятствует удалению самого control block до тех пор, пока есть weak-ссылки, но не препятствует уничтожению объекта при strong == 0.

Итого: в вашем примере решение — заменить одну из shared_ptr на weak_ptr (обычно в B::a), чтобы разорвать цикл владения.

2 Окт в 14:28
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир