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) |
умный указатель с семантикой владения разделяемым объектом (шаблон класса) |
| создаёт общий указатель, который управляет новым объектом (шаблон функции) |