Это позволяет вам получить действительный 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 ):
... такой код не будет работать правильно:
Ни один из двух
shared_ptr
объектов не знает о другом, поэтому оба будут пытаться освободить ресурс, когда они будут уничтожены. Это обычно приводит к проблемам.Точно так же, если функции-члену нужен
shared_ptr
объект, которому принадлежит объект, к которому она вызывается, она не может просто создать объект на лету:Этот код имеет ту же проблему, что и предыдущий пример, хотя и в более тонкой форме. Когда он построен,
shared_pt
объект rsp1
владеет вновь выделенным ресурсом. Код внутри функции-членаS::dangerous
не знает об этомshared_ptr
объекте, поэтомуshared_ptr
возвращаемый объект отличается от негоsp1
. Копирование новогоshared_ptr
объекта вsp2
не помогает; когдаsp2
выходит из области действия, он освобождает ресурс, а когдаsp1
выходит из области действия, он освобождает ресурс снова.Чтобы избежать этой проблемы, используйте шаблон класса
enable_shared_from_this
. Шаблон принимает один аргумент типа шаблона, который является именем класса, который определяет управляемый ресурс. Этот класс, в свою очередь, должен быть публично получен из шаблона; как это:Когда вы делаете это, имейте в виду, что объект, к которому вы обращаетесь,
shared_from_this
должен принадлежатьshared_ptr
объекту. Это не сработает:источник
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
хрупка с самого начала, так как смысл в том, чтобы иметь возможность получить ashared_ptr<T>
от aT*
, но на самом деле, когда вы получаете указатель,T* t
обычно небезопасно предполагать, что что-то о нем уже передано или нет, и сделать неправильное предположение UB.Обратите внимание, что использование boost :: intrusive_ptr не страдает от этой проблемы. Часто это более удобный способ обойти эту проблему.
источник
enable_shared_from_this
позволяет работать с API, который специально принимаетshared_ptr<>
. На мой взгляд, такой API, как правило, делает неправильно, так как лучше, чтобы в стеке было что-то более высокое, но если вы вынуждены работать с таким API, это хороший вариант.Это точно так же в c ++ 11 и более поздних версиях: он позволяет включить возможность возврата
this
в качестве общего указателя, посколькуthis
дает вам необработанный указатель.другими словами, это позволяет вам превращать код следующим образом
в это:
источник
shared_ptr
. Возможно, вы захотите изменить интерфейс, чтобы убедиться, что это так.std::shared_ptr<Node> getParent const()
, чтобы я обычно выставлял это какNodePtr getParent const()
вместо. Если вам абсолютно необходим доступ к внутреннему необработанному указателю (лучший пример: работа с библиотекой C), то естьstd::shared_ptr<T>::get
то, о чем я не хочу упоминать, потому что я использовал этот обработчик необработанных указателей слишком много раз по неправильной причине.Другой способ - добавить
weak_ptr<Y> m_stub
участника вclass Y
. Затем написать:Полезно, когда вы не можете изменить класс, из которого вы производите (например, расширение библиотеки других людей). Не забудьте инициализировать элемент, например, с помощью
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
уже обеспечивает.