Назад к вопросам
Middle+
105
questionbank

Как можно вызвать утечки памяти при использовании 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