Как можно вызвать утечки памяти при использовании std::shared_ptr?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Утечка памяти при использовании std::shared_ptr чаще всего вызвана циклическими ссылками. Когда два или более объекта std::shared_ptr ссылаются друг на друга, их счетчики ссылок могут оставаться ненулевыми, даже если нет внешних ссылок на эти объекты. Это не позволяет уничтожить объекты и высвободить связанную с ними память.
Пример циклической ссылки:
struct Foo {
std::shared_ptr<Foo> other;
};
auto p1 = std::make_shared<Foo>();
auto p2 = std::make_shared<Foo>();
p1->other = p2; // p1 ссылается на p2
p2->other = p1; // p2 ссылается на p1
// После того, как p1 и p2 выйдут из области видимости, их счетчики ссылок
// останутся равными 1 из-за взаимных ссылок, предотвращая деаллокацию.
Для предотвращения таких утечек используется std::weak_ptr. std::weak_ptr хранит невладеющую ссылку на объект, управляемый std::shared_ptr. Он не увеличивает счетчик ссылок.
Пример предотвращения циклической ссылки с std::weak_ptr:
struct Foo {
std::weak_ptr<Foo> other; // Используем weak_ptr
};
auto p1 = std::make_shared<Foo>();
auto p2 = std::make_shared<Foo>();
p1->other = p2;
p2->other = p1;
// Когда p1 и p2 выходят из области видимости, их shared_ptr уничтожаются,
// счетчики ссылок становятся равными 0, и память освобождается.
// weak_ptr не препятствуют этому.
Другой, менее распространенный, способ вызвать утечку памяти с std::shared_ptr — это использование пользовательских удалителей (deleter), которые содержат ошибки или не выполняют свою работу должным образом.
// Ошибочный удалитель, который ничего не делает
void faulty_deleter(int* ptr) {
// Не вызывается delete ptr;
}
auto p = std::shared_ptr<int>(new int(10), faulty_deleter);
// Память, выделенная для int, не будет освобождена при уничтожении p.
Использование исключений во время создания std::shared_ptr может привести к потере владения над необработанным указателем, если он был создан до инициализации std::shared_ptr. Рекомендуется использовать std::make_shared или std::allocate_shared для атомарной аллокации.
int* raw_ptr = new int(5);
// Если здесь произойдет исключение до создания shared_ptr
// Память raw_ptr будет потеряна
// std::shared_ptr<int> sp(raw_ptr); // Создание shared_ptr