Answers:
当您拥有全部时,它使您可以获取有效的shared_ptr
实例。没有它,除非您已经拥有一个成员,否则您将无法获得to 。这个例子来自于enable_shared_from_this Boost文档:this
this
shared_ptr
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
std::shared_ptr
为一个已经由另一个对象管理的对象构造a std::shared_ptr
不会参考内部存储的弱引用,因此会导致未定义的行为。” (en.cppreference.com/w/cpp/memory/enable_shared_from_this)
shared_ptr<Y> q = p
呢?
std::make_shared<T>
。
来自Dobbs博士关于弱指针的文章,我认为这个例子更容易理解(来源: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_from_this()
在第一次调用之前以常规方式创建一个shared_ptr !这真的很容易出错!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();
<- 仅允许在先前共享的对象(即std :: shared_ptr <T>管理的对象)上调用shared_from_this。否则,该行为是不确定的(直到C ++ 17为止)(从C ++ 17起)抛出std :: bad_weak_ptr(由默认构造的weak_this的shared_ptr构造函数抛出)。。因此现实是应该调用它always_dangerous()
,因为您需要知道它是否已经共享。
这是我的解释,从具体的角度来看(最主要的答案不是“点击”我)。*请注意,这是调查Visual Studio 2012随附的shared_ptr和enable_shared_from_this源的结果。也许其他编译器以不同的方式实现enable_shared_from_this ... *
enable_shared_from_this<T>
添加一个私有weak_ptr<T>
实例,T
该实例包含的实例的“ 一个真实引用计数 ” T
。
所以,当你第一次创建一个shared_ptr<T>
到一个新的T *,即T *的内部weak_ptr的获取与1.新的引用计数初始化的shared_ptr
基本备份到这个weak_ptr
。
T
然后可以在其方法中,调用shared_from_this
以获得的一个实例shared_ptr<T>
即背到同一内部存储的参考计数。这样,您总是在一个地方T*
存储ref-count,而不是有多个shared_ptr
彼此不认识的实例,并且每个实例都认为它们是shared_ptr
负责ref-count的实例,并在引用T
时将其删除计数达到零。
So, when you first create...
因为这是一个要求(正如您所说的,在将对象指针传递到shared_ptr ctor之前,不初始化weak_ptr!),如果您是不小心 如果在调用之前未创建shared_ptr,则shared_from_this
您会得到UB-同样,如果创建多个shared_ptr,也会获得UB。你必须以某种方式确保你创建一个shared_ptr 正是一次。
enable_shared_from_this
是脆开始以来的一点是能够得到一个shared_ptr<T>
从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<>
不会删除其指针的,那就太过分了。你可以简单地说,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
,它会保留a weak_ptr
本身(由ctor填充),并shared_ptr
在调用时作为a返回shared_from_this
。换句话说,您正在复制enable_shared_from_this
已经提供的内容。