std::enable_shared_from_this
Определено в заголовочном файле <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 функция-элемент) | |
(C++17) |
возвращает 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 была неяснойи, возможно, нереализуемой |
уточнено и исправлено |
[править] Смотрите также
(C++11) |
умный указатель с семантикой владения разделяемым объектом (шаблон класса) |
создаёт общий указатель, который управляет новым объектом (шаблон функции) |