Пространства имён
Варианты
Действия

std::enable_shared_from_this

Материал из cppreference.com
< cpp‎ | memory
 
 
Библиотека утилит
Языковая поддержка
Поддержка типов (базовые типы, RTTI)
Макросы тестирования функциональности библиотеки (C++20)    
Управление динамической памятью
Программные утилиты
Поддержка сопрограмм (C++20)
Вариативные функции
Трёхстороннее сравнение (C++20)
(C++20)
(C++20)(C++20)(C++20)(C++20)(C++20)(C++20)
Общие утилиты
Дата и время
Функциональные объекты
Библиотека форматирования (C++20)
(C++11)
Операторы отношения (устарело в C++20)
Целочисленные функции сравнения
(C++20)(C++20)(C++20)    
(C++20)
Операции обмена и типа
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
Общие лексические типы
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
Элементарные преобразования строк
(C++17)
(C++17)
 
Динамическое управление памятью
no section name
Ограниченные алгоритмы неинициализированной памяти
no section name
Поддержка сбора мусора
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)



no section name
 
 
Определено в заголовочном файле <memory>
template< class T > class enable_shared_from_this;
(начиная с C++11)

std::enable_shared_from_this позволяет объекту t, которым в настоящее время управляет std::shared_ptr с именем pt, безопасно генерировать дополнительные экземпляры pt1, pt2, ... класса std::shared_ptr, которые совместно владеют t вместе с pt.

Открытое наследование от std::enable_shared_from_this<T> предоставляет тип T с функцией-элементом shared_from_this. Если объект t типа T управляется std::shared_ptr<T> с именем pt, то вызов T::shared_from_this вернёт новый std::shared_ptr<T>, который будет разделять владение объектом t вместе с pt.

Содержание

[править] Функции-элементы

создаёт объект enable_shared_from_this
(protected функция-элемент)
уничтожает объект enable_shared_from_this
(protected функция-элемент)
возвращает ссылку на *this
(protected функция-элемент)
возвращает std::shared_ptr, который разделяет владение *this
(public функция-элемент)
возвращает std::weak_ptr, который разделяет владение *this
(public функция-элемент)

[править] Объекты-элементы

Имя элемента Определение
weak-this (exposition only) объект std::weak_ptr, отслеживающий блок управления первого общего владельца *this.

[править] Примечание

Распространённой реализацией enable_shared_from_this является хранение слабой ссылки (например, std::weak_ptr) на this. Для наглядности слабая ссылка называется weak-this и рассматривается как mutable элемент std::weak_ptr.

Конструкторы std::shared_ptr обнаруживают наличие однозначного и доступного (т.е. открытое наследование обязательно) базового класса enable_shared_from_this и присваивают вновь созданный std::shared_ptr элементу weak-this, если он ещё не принадлежит живому std::shared_ptr. Создание std::shared_ptr для объекта, который уже управляется другим std::shared_ptr, не будет обращаться к weak-this и, следовательно, будет приводить к неопределённому поведению.

Разрешается вызывать shared_from_this только для предыдущего общего объекта, т.е. для объекта, управляемого std::shared_ptr<T>. Иначе генерируется std::bad_weak_ptr (конструктором shared_ptr из созданного по умолчанию weak-this).

enable_shared_from_this обеспечивает безопасную альтернативу такому выражению, как std::shared_ptr<T>(this), которое может привести к тому, что this будет удалено более одного раза несколькими владельцами, которые не знают друг о друге (смотрите пример ниже).

[править] Пример

#include <iostream>
#include <memory>
 
class Good : public std::enable_shared_from_this<Good>
{
public:
    std::shared_ptr<Good> getptr()
    {
        return shared_from_this();
    }
};
 
class Best : public std::enable_shared_from_this<Best>
{
public:
    std::shared_ptr<Best> getptr()
    {
        return shared_from_this();
    }
    // Нет открытого конструктора, только фабричная функция, поэтому
    // нет возможности заставить getptr возвращать nullptr.
    [[nodiscard]] static std::shared_ptr<Best> create()
    {
        // Не используем std::make_shared<Best>, потому что конструктор является закрытым.
        return std::shared_ptr<Best>(new Best());
    }
private:
    Best() = default;
};
 
 
struct Bad
{
    std::shared_ptr<Bad> getptr()
    {
        return std::shared_ptr<Bad>(this);
    }
    ~Bad() { std::cout << "Bad::~Bad() вызван\n"; }
};
 
void testGood()
{
    // Хорошо: два shared_ptr используют один и тот же объект
    std::shared_ptr<Good> good0 = std::make_shared<Good>();
    std::shared_ptr<Good> good1 = good0->getptr();
    std::cout << "good1.use_count() = " << good1.use_count() << '\n';
}
 
void misuseGood()
{
    // Плохо: shared_from_this вызывается, когда std::shared_ptr не владеет
    // вызывающей стороной
    try
    {
        Good not_so_good;
        std::shared_ptr<Good> gp1 = not_so_good.getptr();
    }
    catch (std::bad_weak_ptr& e)
    {
        // неопределённое поведение (до C++17) и генерируется std::bad_weak_ptr
        // (начиная с C++17)
        std::cout << e.what() << '\n';
    }
}
 
void testBest()
{
    // Best: То же самое, но нельзя разместить его в стеке:
    std::shared_ptr<Best> best0 = Best::create();
    std::shared_ptr<Best> best1 = best0->getptr();
    std::cout << "best1.use_count() = " << best1.use_count() << '\n';
 
    // Best stackBest; // <- Не будет компилироваться, потому что Best::Best()
                       //    является закрытым.
}
 
void testBad()
{
    // Bad, каждый shared_ptr считает себя единственным владельцем объекта
    std::shared_ptr<Bad> bad0 = std::make_shared<Bad>();
    std::shared_ptr<Bad> bad1 = bad0->getptr();
    std::cout << "bad1.use_count() = " << bad1.use_count() << '\n';
} // Неопределённое поведение: двойное удаление Bad
 
int main()
{
    testGood();
    misuseGood();
 
    testBest();
 
    testBad();
}

Возможный вывод:

good1.use_count() = 2
bad_weak_ptr
best1.use_count() = 2
bad1.use_count() = 1
Bad::~Bad() вызван
Bad::~Bad() вызван
*** glibc detected *** ./test: double free or corruption

[править] Отчёты о дефектах

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

Номер Применён Поведение в стандарте Корректное поведение
LWG 2529 C++11 спецификация для enable_shared_from_this была неясной
и, возможно, нереализуемой
уточнено и исправлено

[править] Смотрите также

умный указатель с семантикой владения разделяемым объектом
(шаблон класса) [править]
создаёт общий указатель, который управляет новым объектом
(шаблон функции) [править]