Menu
Coddy logo textTech

Умные указатели в C++

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 15 из 104.

Умные указатели — это классы-обертки, которые автоматически управляют памятью в куче за вас. Они устраняют необходимость в ручных вызовах delete, очищая память, когда указатель выходит из области видимости.

Подключите заголовочный файл <memory> для использования умных указателей.

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

#include <memory>

std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack();  // Используется как обычный указатель

// Нельзя копировать, но можно передать владение
std::unique_ptr<Player> player2 = std::move(player);
// player теперь равен nullptr

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

std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1;  // Оба владеют ресурсом

std::cout << p1.use_count();  // Выводит 2

weak_ptr — невладеющий наблюдатель для shared_ptr. Полезен для разрыва циклических ссылок.

std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;

if (auto locked = weak.lock()) {  // Преобразование в shared_ptr
    locked->attack();
}

Используйте unique_ptr по умолчанию для единоличного владения. Используйте shared_ptr, когда нескольким объектам необходимо совместно использовать ресурс. Используйте weak_ptr для наблюдения без продления времени жизни.

Предпочитайте make_unique и make_shared вместо обычного new.

challenge icon

Задание

Легко

Давайте создадим систему обмена документами, которая демонстрирует различные модели владения умными указателями. Вы увидите, как unique_ptr обеспечивает исключительное владение, как shared_ptr позволяет иметь несколько владельцев и как weak_ptr может наблюдать за объектом, не влияя на время его жизни.

Вы создадите два файла для организации вашего кода:

  • Document.h: Определите класс Document, который представляет документ для совместного использования. Он должен иметь:
    • Приватный атрибут title (строка)
    • Конструктор, который принимает заголовок и выводит "Document created: <title>"
    • Деструктор, который выводит "Document deleted: <title>"
    • Метод getTitle(), который возвращает заголовок
  • main.cpp: Продемонстрируйте все три типа умных указателей, прочитав заголовок документа из входных данных, а затем:
    • Создайте unique_ptr для Document с введенным заголовком
    • Выведите "Unique owner: <title>"
    • Передайте владение другому unique_ptr с помощью std::move
    • Выведите "Ownership transferred"
    • Создайте shared_ptr для нового Document с заголовком "SharedDoc"
    • Создайте второй shared_ptr, указывающий на тот же документ
    • Выведите "Shared owners: <count>", используя use_count()
    • Создайте weak_ptr из одного из shared-указателей
    • Используйте lock() для доступа к документу через weak-указатель и выведите "Weak access: <title>"

Не забудьте включить <memory> для работы с умными указателями. Используйте std::make_unique и std::make_shared для создания ваших умных указателей. Сообщения деструктора помогут вам увидеть, когда каждый документ автоматически удаляется в конце программы.

Включите ваш заголовочный файл в main.cpp с помощью #include "Document.h".

Шпаргалка

Умные указатели — это классы-обертки, которые автоматически управляют памятью в куче. Для их использования подключите заголовочный файл <memory>.

unique_ptr

Эксклюзивное владение — только один указатель может владеть ресурсом в данный момент времени:

std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack();  // Используйте как обычный указатель

// Передача владения с помощью std::move
std::unique_ptr<Player> player2 = std::move(player);
// player теперь равен nullptr

shared_ptr

Разделяемое владение — несколько указателей могут владеть одним и тем же ресурсом. Память освобождается, когда уничтожается последний владелец:

std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1;  // Оба владеют ресурсом

std::cout << p1.use_count();  // Выводит 2

weak_ptr

Не владеющий наблюдатель для shared_ptr. Полезен для разрыва циклических ссылок:

std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;

if (auto locked = weak.lock()) {  // Преобразование в shared_ptr
    locked->attack();
}

Лучшие практики: Используйте unique_ptr по умолчанию для единоличного владения. Используйте shared_ptr, когда нескольким объектам необходимо совместно использовать ресурс. Используйте weak_ptr для наблюдения без продления времени жизни. Отдавайте предпочтение make_unique и make_shared вместо обычного new.

Попробуйте сами

#include <iostream>
#include <memory>
#include <string>
#include "Document.h"

using namespace std;

int main() {
    string inputTitle;
    cin >> inputTitle;

    // TODO: Создайте unique_ptr для Document с inputTitle
    // используя std::make_unique

    // TODO: Выведите "Unique owner: <title>"

    // TODO: Передайте владение другому unique_ptr, используя std::move

    // TODO: Выведите "Ownership transferred"

    // TODO: Создайте shared_ptr для нового Document с заголовком "SharedDoc"
    // используя std::make_shared

    // TODO: Создайте второй shared_ptr, указывающий на тот же документ

    // TODO: Выведите "Shared owners: <count>", используя use_count()

    // TODO: Создайте weak_ptr из одного из shared-указателей

    // TODO: Используйте lock() для доступа к документу через weak_ptr
    // и выведите "Weak access: <title>"

    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование