Відповіді:
Це дозволяє вам отримати дійсний shared_ptr
екземпляр this
, коли все, що у вас є this
. Без нього ви не мали б ніякого способу отримувати shared_ptr
до this
, якщо ви вже мали один в якості члена. Цей приклад із документації для підвищення для enable_shared_from_this :
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
Метод f()
повертає дійсний shared_ptr
, навіть не маючи примірника члена. Зауважте, що ви не можете просто зробити це:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Спільний покажчик, який повернувся, матиме інший підрахунок посилань від "належного", і один із них втратить і матиме звисаючу посилання, коли об'єкт буде видалений.
enable_shared_from_this
став частиною стандарту C ++ 11. Ви також можете отримати його як звідти, так і з прискорення.
std::shared_ptr
конструктор на сирому вказівнику, якщо він успадковує з std::enable_shared_from_this
. Я не знаю, чи було оновлено семантику Boost, щоб це підтримати.
std::shared_ptr
для об'єкта, яким вже керує інший std::shared_ptr
, не звертається до внутрішньо збережених слабких посилань і, таким чином, призведе до невизначеної поведінки". ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )
shared_ptr<Y> q = p
?
std::make_shared<T>
.
зі статті доктора Доббса про слабкі покажчики, я думаю, що цей приклад легше зрозуміти (джерело: http://drdobbs.com/cpp/184402026 ):
... такий код не працює належним чином:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Жоден з двох shared_ptr
об'єктів не знає про інший, тому обидва намагатимуться звільнити ресурс, коли вони будуть знищені. Зазвичай це призводить до проблем.
Аналогічно, якщо функції-члену потрібен shared_ptr
об'єкт, який належить об'єкту, до якого його викликають, він не може просто створити об'єкт на льоту:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
Цей код має ту саму проблему, що і в попередньому прикладі, хоча у більш тонкій формі. Коли він побудований, shared_pt
r об'єкту sp1
належить щойно виділений ресурс. Код всередині функції-члена S::dangerous
не знає про цей shared_ptr
об'єкт, тому shared_ptr
об'єкт, від якого він повертається, відрізняється від sp1
. Копіювання нового shared_ptr
об’єкта sp2
не допомагає; коли sp2
вийде за рамки, він вивільнить ресурс, а коли sp1
вийде за межі, знову випустить ресурс.
Спосіб уникнути цієї проблеми - використовувати шаблон класу enable_shared_from_this
. Шаблон приймає один аргумент типу шаблону, який є іменем класу, який визначає керований ресурс. Цей клас, у свою чергу, повинен бути публічно виведений із шаблону; подобається це:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
Роблячи це, майте на увазі, що об’єкт, на який ви телефонуєте, shared_from_this
повинен належати shared_ptr
об'єкту. Це не спрацює:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
shared_ptr<S> sp1(new S);
цього можна віддати перевагу shared_ptr<S> sp1 = make_shared<S>();
, див., Наприклад, stackoverflow.com/questions/18301511/…
shared_ptr<S> sp2 = p->not_dangerous();
оскільки підводний камінь тут полягає в тому, що ви повинні створити shared_ptr звичайним способом, перш ніж shared_from_this()
вперше дзвонити ! Це дійсно легко помилитися! До C ++ 17 це UB зателефонувати , shared_from_this()
перш ніж точно один shared_ptr був створений звичайним способом: auto sptr = std::make_shared<S>();
або shared_ptr<S> sptr(new S());
. Вдячно від C ++ 17 далі буде кидати це.
S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- Дозволено викликати shared_from_this тільки на раніше спільному об'єкті, тобто на об'єкті, керованому std :: shared_ptr <T>. В іншому випадку поведінка не визначена (до C ++ 17) std :: bad_weak_ptr буде викинуто (конструктором shared_ptr з конструюваного за замовчуванням слабкого_ це) (оскільки C ++ 17). . Тож реальність така, що її слід називати always_dangerous()
, тому що вам потрібні знання про те, чи вона вже поділилася чи ні.
Ось моє пояснення з точки зору гайок і болтів (головна відповідь не «клацала» зі мною). * Зауважте, що це результат дослідження джерела для shared_ptr та enable_shared_from_this, який поставляється з Visual Studio 2012. Можливо, інші компілятори реалізують enable_shared_from_this по-різному ... *
enable_shared_from_this<T>
додає приватний weak_ptr<T>
екземпляр, до T
якого міститься " один справжній посилання " для екземпляра T
.
Отже, коли ви вперше створюєте shared_ptr<T>
на новій T *, внутрішній слабкий_ptr T * ініціалізується зі зворотним рахунком 1. Новий в shared_ptr
основному спирається на це weak_ptr
.
T
може, у своїх методах, викликати shared_from_this
отримання екземпляра shared_ptr<T>
цього списку на той самий внутрішньо збережений контрольний номер . Таким чином, у вас завжди є одне місце, де T*
зберігається повторний підрахунок, а не декілька shared_ptr
екземплярів, які не знають один про одного, і кожен думає, що вони є тим, shared_ptr
хто відповідає за підрахунок посилання T
та видалення його, коли їх посилання -кілька досягає нуля.
So, when you first create...
тому, що це вимога (як ви говорите, слабкий_ptr не ініціалізується, поки ви не передасте вказівник об'єктів у спільний_ptr ctor!), І ця вимога полягає в тому, де речі можуть жахливо помилитися, якщо ви не обережні. Якщо ви не створили no shared_ptr перед викликом, shared_from_this
ви отримуєте UB - так само, якщо ви створите більше одного shared_ptr, ви також отримаєте UB. Ви повинні якось переконатися, що ви створили shared_ptr рівно один раз.
enable_shared_from_this
крихка для початку, оскільки справа полягає в тому, щоб мати змогу отримати shared_ptr<T>
від a T*
, але насправді, коли ви отримуєте вказівник, T* t
як правило, не безпечно припускати, що про нього вже поділяють чи ні, і зробити неправильну здогаду UB.
Зауважте, що використання boost :: intrusive_ptr не страждає від цієї проблеми. Це часто більш зручний спосіб подолати цю проблему.
enable_shared_from_this
дозволяє працювати з API, який спеціально приймає shared_ptr<>
. На мою думку, такий API зазвичай робить Doing It Wrong (оскільки краще дозволити чомусь вище в стеку володіти пам'яттю), але якщо ви змушені працювати з таким API, це хороший варіант.
Це точно так само в c ++ 11 і пізніших версіях: це увімкнути можливість повернення this
як спільний покажчик, оскільки this
дає вам необроблений покажчик.
інакше кажучи, це дозволяє вам перетворити такий код
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
в це:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};
shared_ptr
. Ви можете змінити інтерфейс, щоб переконатися, що це так.
std::shared_ptr<Node> getParent const()
я, як правило, виставляю це як NodePtr getParent const()
замість цього. Якщо вам абсолютно потрібен доступ до внутрішнього необробленого вказівника (найкращий приклад: робота з бібліотекою С), є std::shared_ptr<T>::get
те, про що я ненавиджу, тому що я цей необроблений аксесуар вказівника використовував занадто багато разів з неправильної причини.
Інший спосіб - додати weak_ptr<Y> m_stub
члена до class Y
. Тоді пиши:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
Корисно, коли ви не можете змінити клас, з якого ви походите (наприклад, розширення бібліотеки інших людей). Не забувайте ініціалізувати елемент, наприклад m_stub = shared_ptr<Y>(this)
, його дія діє навіть під час конструктора.
Добре, якщо в ієрархії спадкування є більше таких заглушок, як це, це не завадить знищити об'єкт.
Редагувати: Як правильно вказав користувач nobar, код знищить об'єкт Y, коли призначення буде закінчено, а тимчасові змінні знищені. Тому моя відповідь неправильна.
shared_ptr<>
який не видаляє його покажчик, це зайве. Ви можете просто сказати, return shared_ptr<Y>(this, no_op_deleter);
де no_op_deleter
знаходиться об'єкт унарної функції, який Y*
нічого не робить.
m_stub = shared_ptr<Y>(this)
побудує та негайно знищить з цього тимчасовий спільний_ptr. Коли ця заява закінчена, this
буде видалено, а всі наступні посилання будуть звисати.
enable_shared_from_this
, вона зберігає weak_ptr
себе (заповнений ctor), повертається як a, shared_ptr
коли ви телефонуєте shared_from_this
. Іншими словами, ви дублюєте те, що enable_shared_from_this
вже передбачено.