Умные указатели в C++: unique_ptr, shared_ptr, weak_ptr

Автор: | 13 июня, 2025

Умные указатели (smart pointers) — это объекты-обертки для «сырых» указателей (raw pointers), которые автоматически управляют временем жизни динамически выделенной памяти. Они были введены в стандарт C++11 для решения проблем с утечками памяти и висячими указателями, которые часто возникают при ручном управлении памятью.

Основные типы умных указателей

Стандартная библиотека C++ предоставляет три основных типа умных указателей:

  1. std::unique_ptr — уникальное владение ресурсом
  2. std::shared_ptr — разделяемое владение ресурсом
  3. std::weak_ptr — слабая ссылка на ресурс, управляемый shared_ptr

1. std::unique_ptr

unique_ptr — это умный указатель с эксклюзивным владением ресурсом. В один момент времени только один unique_ptr может владеть определенным ресурсом.

Особенности:

  • Невозможно копировать (удаленный конструктор копирования)
  • Можно только перемещать (move semantics)
  • Автоматически освобождает память при выходе из области видимости
  • Меньшие накладные расходы по сравнению с shared_ptr

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

#include <memory>

void example() {
    // Создание unique_ptr
    std::unique_ptr<int> ptr(new int(10));
    
    // Можно использовать как обычный указатель
    *ptr = 20;
    
    // Передача владения
    std::unique_ptr<int> ptr2 = std::move(ptr);
    
    // ptr теперь nullptr
    if (!ptr) {
        std::cout << "ptr is empty\n";
    }
    
    // Память автоматически освобождается при выходе из области видимости
}

2. std::shared_ptr

shared_ptr реализует семантику разделяемого владения ресурсом. Несколько shared_ptr могут владеть одним и тем же ресурсом, и память освобождается, когда последний shared_ptr выходит из области видимости.

Особенности:

  • Использует подсчет ссылок (reference counting)
  • Поддерживает копирование
  • Большие накладные расходы по сравнению с unique_ptr
  • Возможность циклических ссылок

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

#include <memory>

void example() {
    // Создание shared_ptr
    std::shared_ptr<int> ptr1(new int(10));
    
    // Копирование - увеличивает счетчик ссылок
    std::shared_ptr<int> ptr2 = ptr1;
    
    // Оба указателя владеют одним ресурсом
    std::cout << *ptr1 << " " << *ptr2 << "\n";
    
    // Счетчик ссылок уменьшается при выходе из области видимости
    // Память освобождается, когда счетчик достигает 0
}

3. std::weak_ptr

weak_ptr — это слабая ссылка на объект, которым владеет shared_ptr. Он не увеличивает счетчик ссылок и используется для решения проблемы циклических ссылок.

Особенности:

  • Не владеет ресурсом
  • Не увеличивает счетчик ссылок
  • Можно проверить, жив ли еще объект
  • Для доступа к объекту нужно преобразовать в shared_ptr

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

#include <memory>

void example() {
    std::shared_ptr<int> shared = std::make_shared<int>(10);
    std::weak_ptr<int> weak = shared;
    
    // Проверка, доступен ли еще объект
    if (auto temp = weak.lock()) {
        std::cout << *temp << "\n";
    } else {
        std::cout << "Object is destroyed\n";
    }
    
    shared.reset(); // освобождаем ресурс
    
    if (weak.expired()) {
        std::cout << "Object is destroyed\n";
    }
}

Сравнение умных указателей

Характеристикаunique_ptrshared_ptrweak_ptr
ВладениеЭксклюзивноеРазделяемоеНет владения
КопируемостьНет (только move)ДаДа
Счетчик ссылокНетДаНет (но следит)
Накладные расходыМинимальныеВысокиеСредние
Основное назначениеЕдинственный владелецМножество владельцевРазрыв циклических ссылок

Рекомендации по использованию

  1. Предпочитайте unique_ptr, когда ресурсом должен владеть только один объект
  2. Используйте shared_ptr, когда необходимо разделяемое владение
  3. Применяйте weak_ptr для разрыва циклических ссылок между shared_ptr
  4. Избегайте смешивания сырых указателей с умными
  5. Используйте make_unique и make_shared для создания умных указателей (появились в C++14 и C++11 соответственно)
// Лучше так:
auto ptr = std::make_unique<int>(10);
auto ptr2 = std::make_shared<int>(20);

// Чем так:
std::unique_ptr<int> ptr(new int(10));
std::shared_ptr<int> ptr2(new int(20));

Заключение

Умные указатели — это мощный инструмент современного C++, который значительно упрощает управление памятью и помогает избежать многих распространенных ошибок. Правильное использование unique_ptr, shared_ptr и weak_ptr делает код более безопасным, читаемым и поддерживаемым.