fid*_*ido 332 c++ boost tr1 boost-asio
我在enable_shared_from_this阅读Boost.Asio示例时跑了过来,在阅读完文档之后,我仍然因为如何正确使用它而迷失了方向.有人可以给我一个例子和/或说明何时使用这个课程是有道理的.
180*_*ION 347
它可以让你获得一个有效的shared_ptr实例this,当你拥有它时this.没有它,你就没有得到一个的方式shared_ptr来this,除非你已经有一个为会员.这个例子来自enable_shared_from_this的boost文档:
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
}
Run Code Online (Sandbox Code Playgroud)
方法f()返回一个有效的f(),即使它没有成员实例.请注意,您不能简单地这样做:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Run Code Online (Sandbox Code Playgroud)
返回的共享指针将具有与"正确"引用不同的引用计数,并且当删除对象时,其中一个将最终丢失并保持悬空引用.
shared_ptr已成为C++ 11标准的一部分.你也可以从那里以及从中获得它.
小智 187
来自Dobbs博士关于弱指针的文章,我认为这个例子更容易理解(来源:http://drdobbs.com/cpp/184402026):
...这样的代码无法正常工作:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Run Code Online (Sandbox Code Playgroud)
这两个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;
}
Run Code Online (Sandbox Code Playgroud)
此代码与前面的示例具有相同的问题,但是以更微妙的形式.构造时,shared_ptr对象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;
}
Run Code Online (Sandbox Code Playgroud)
执行此操作时,请记住,您调用的对象shared_from_this必须由shared_ptr对象拥有.这不起作用:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Run Code Online (Sandbox Code Playgroud)
mac*_*nir 28
这是我的解释,从坚果和螺栓的角度来看(最高的答案并没有'点击'跟我一起).*请注意,这是调查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负责重新计数T并在他们的引用时删除它-count达到零.
在一种情况下,我发现 enable_shared_from_this 非常有用:使用异步回调时的线程安全。
想象一下 Client 类有一个 AsynchronousPeriodicTimer 类型的成员:
struct AsynchronousPeriodicTimer
{
// call this periodically on some thread...
void SetCallback(std::function<void(void)> callback);
void ClearCallback(); // clears the callback
}
struct Client
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
_timer->SetCallback(
[this]
()
{
assert(this); // what if 'this' is already dead because ~Client() has been called?
std::cout << ++_counter << '\n';
}
);
}
~Client()
{
// clearing the callback is not in sync with the timer, and can actually occur while the callback code is running
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
int main()
{
auto timer = std::make_shared<AsynchronousPeriodicTimer>();
{
auto client = std::make_shared<Client>(timer);
// .. some code
// client dies here, there is a race between the client callback and the client destructor
}
}
Run Code Online (Sandbox Code Playgroud)
客户端类订阅一个回调函数给周期性定时器。一旦客户端对象超出范围,回调和析构函数之间就会出现竞争条件。可以使用悬空指针调用回调!
解决方法:使用 enable_shared_from_this:
struct Client : std::enable_shared_from_this<Client>
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
}
void Init()
{
auto captured_self = weak_from_this(); // weak_ptr to avoid cyclic references with shared_ptr
_timer->SetCallback(
[captured_self]
()
{
if (auto self = captured_self.lock())
{
// 'this' is guaranteed to be non-nullptr. we managed to promote captured_self to a shared_ptr
std::cout << ++self->_counter << '\n';
}
}
);
}
~Client()
{
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
Run Code Online (Sandbox Code Playgroud)
该Init方法与构造函数分离,因为初始化过程enable_shared_from_this直到构造函数退出才结束。因此,额外的方法。从构造函数中订阅异步回调通常是不安全的,因为回调可能会访问未初始化的字段。