Дан кусок кода на C++ с использованием умных указателей (shared_ptr и weak_ptr), в котором происходит утечка памяти из‑за цикла ссылок — объясните проблему и предложите исправление
Коротко: утечка возникает из‑за цикла сильных (shared_ptr) ссылок: два (или более) объекта держат shared_ptr друг на друга, поэтому счётчики ссылок никогда не доходят до 0 \,0\,0 и деструкторы не вызываются. Решение — разорвать цикл, заменив хотя бы одну сторону на weak_ptr. Пример (проблема) ```cpp #include
#include
using namespace std; struct B; struct A { shared_ptr b; ~A(){ cout << "~A\n"; } }; struct B { shared_ptr a; ~B(){ cout << "~B\n"; } }; int main() { auto a = make_shared(); auto b = make_shared(); a->b = b; // увеличивает счётчик b b->a = a; // увеличивает счётчик a // при выходе из main локальные a и b уничтожаются, // но объекты всё ещё держат друг друга => утечка } ``` Пояснение счётчиков: до присвоений у каждого счётчик 1 \,1\;1. После присвоений: a.use_count()=2\,a.use\_count() = 2a.use_count()=2, b.use_count()=2\,b.use\_count() = 2b.use_count()=2. При выходе из main локальные указатели снимаются, но счётчики остаются 1\,11 из‑за взаимных ссылок, и до 0\,00 они не доходят. Правильное исправление — использовать weak_ptr на одной стороне: ```cpp #include
#include
using namespace std; struct B; struct A { weak_ptr b; // weak_ptr ломает цикл ~A(){ cout << "~A\n"; } }; struct B { shared_ptr a; ~B(){ cout << "~B\n"; } }; int main() { auto a = make_shared(); auto b = make_shared(); a->b = b; // не увеличивает счётчик strong у b b->a = a; // увеличивает счётчик у a // при выходе из main dtor вызовутся корректно // если нужно получить shared_ptr из weak_ptr: if (auto bp = a->b.lock()) { // безопасно использовать bp } } ``` Альтернативы/замечания: - Можно явно разрывать цикл через reset(), но это менее удобно и подвержено ошибкам. - weak_ptr не владеет объектом; чтобы получить доступ, используйте lock() и проверяйте результат. - При создании shared_ptr из this используйте enable_shared_from_this, чтобы не создавать отдельные владеющие указатели.
Пример (проблема)
```cpp
#include #include using namespace std;
struct B;
struct A {
shared_ptr b;
~A(){ cout << "~A\n"; }
};
struct B {
shared_ptr a;
~B(){ cout << "~B\n"; }
};
int main() {
auto a = make_shared();
auto b = make_shared();
a->b = b; // увеличивает счётчик b
b->a = a; // увеличивает счётчик a
// при выходе из main локальные a и b уничтожаются,
// но объекты всё ещё держат друг друга => утечка
}
```
Пояснение счётчиков: до присвоений у каждого счётчик 1 \,1\;1. После присвоений: a.use_count()=2\,a.use\_count() = 2a.use_count()=2, b.use_count()=2\,b.use\_count() = 2b.use_count()=2. При выходе из main локальные указатели снимаются, но счётчики остаются 1\,11 из‑за взаимных ссылок, и до 0\,00 они не доходят.
Правильное исправление — использовать weak_ptr на одной стороне:
```cpp
#include #include using namespace std;
struct B;
struct A {
weak_ptr b; // weak_ptr ломает цикл
~A(){ cout << "~A\n"; }
};
struct B {
shared_ptr a;
~B(){ cout << "~B\n"; }
};
int main() {
auto a = make_shared();
auto b = make_shared();
a->b = b; // не увеличивает счётчик strong у b
b->a = a; // увеличивает счётчик у a
// при выходе из main dtor вызовутся корректно
// если нужно получить shared_ptr из weak_ptr:
if (auto bp = a->b.lock()) {
// безопасно использовать bp
}
}
```
Альтернативы/замечания:
- Можно явно разрывать цикл через reset(), но это менее удобно и подвержено ошибкам.
- weak_ptr не владеет объектом; чтобы получить доступ, используйте lock() и проверяйте результат.
- При создании shared_ptr из this используйте enable_shared_from_this, чтобы не создавать отдельные владеющие указатели.