Ответы:
Это позволяет вам получить действительный 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, созданного по умолчанию weak_this) (начиная с 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 *, внутренний слабый_птр этого T * инициализируется с повторным счетом 1. Новое в shared_ptrосновном возвращается к этому weak_ptr.
Tзатем может в своих методах вызывать shared_from_thisдля получения экземпляра этого объекта shared_ptr<T>тот же внутренний счетчик ссылок . Таким образом, у вас всегда есть одно место, где T*хранится ref-count, а не несколько shared_ptrэкземпляров, которые не знают друг о друге, и каждый думает, shared_ptrчто именно он отвечает за подсчет Tи удаление его, когда их ref Счет достигает нуля.
So, when you first create...том, что это требование (как вы говорите, weak_ptr не инициализируется до тех пор, пока вы не передадите указатель объектов в ctor shared_ptr!), И это требование - то, где все может пойти не так, если вы не осторожно Если вы не создали shared_ptr перед вызовом, shared_from_thisвы получите UB - аналогично, если вы создадите более одного shared_ptr, вы получите и UB. Вы должны как-то убедиться, что вы создали shared_ptr ровно один раз.
enable_shared_from_thisхрупка с самого начала, так как смысл в том, чтобы иметь возможность получить a shared_ptr<T>от a T*, но на самом деле, когда вы получаете указатель, T* tобычно небезопасно предполагать, что что-то о нем уже передано или нет, и сделать неправильное предположение UB.
Обратите внимание, что использование boost :: intrusive_ptr не страдает от этой проблемы. Часто это более удобный способ обойти эту проблему.
enable_shared_from_thisпозволяет работать с API, который специально принимает shared_ptr<>. На мой взгляд, такой API, как правило, делает неправильно, так как лучше, чтобы в стеке было что-то более высокое, но если вы вынуждены работать с таким 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()вместо. Если вам абсолютно необходим доступ к внутреннему необработанному указателю (лучший пример: работа с библиотекой C), то есть 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<>который не удаляет его pointee, это излишне. Вы можете просто сказать, return shared_ptr<Y>(this, no_op_deleter);где no_op_deleterунарный функциональный объект берет Y*и ничего не делает.
m_stub = shared_ptr<Y>(this)построит и немедленно уничтожит временный shared_ptr из этого. Когда это утверждение закончится, thisбудут удалены и все последующие ссылки будут болтаться.
enable_shared_from_this, она сохраняет weak_ptrсебя (заполняется ctor), возвращаемую как shared_ptrпри вызове shared_from_this. Другими словами, вы дублируете то, что enable_shared_from_thisуже обеспечивает.