C++11标准库的一个工具类enable_shared_from_this<T>的作用及原理分析
介绍
enable_shared_from_this
class T : public enable_shared_from_this<T> {
};
类T会继承到一个公有方法
shared_ptr<T> shared_from_this();
要在T类中使用该函数,是继承enable_shared_from_this
要解决的问题
如果一个程序中,对象内存的生命周期的全部由智能指针来管理。在这种情况下,要在一个类的成员函数中,对外部返回this指针成了一个很棘手的问题。
具体代码如下:
class obj {
public:
obj* getRawThis() {
return this; // 这样做语法上没有问题,但是破坏了智能指针的语义了,使用了原生指针
}
shared_ptr<obj> getSharedCopy() {
return shared_ptr<obj>(this); // 这样做程序会崩掉,这个临时变量在函数完毕后会被析构掉,会执行delete this,
}
shared_ptr<obj>& getSharedRef() {
if(p == nullptr) {
p = new shared_ptr<obj>(this);
}
return *p; // 这样做就更憨了,obj对象本身持有了包覆自己this指针的shared_ptr,那么他们的生命周期是“同步”的,这样被管理的obj永远不会释放
}
// 当然,从上面也可以看出,实际上核心的问题,就是:
// 1. 如果采用返回拷贝,那么临时变量会把this析构掉
// 2. 如果采用返回引用,那么obj对象本身必须持有一个管理他的shared_ptr,这样会导致obj永远不会被释放
// 可用的解决方案有以下两种
void getSharedFromParam(shared_ptr<obj>& p) {
p.reset(this); // 通过函数参数来实现规避掉拷贝语义,但是这样太过于丑陋
}
shared_ptr<obj> getSharedFromThis() {
return shared_from_this(); // 这是C++11推荐的方法,可以实现安全的返回一个shared_ptr<this>,返回到外面的shared_ptr的拷贝的use_count是1,不用担心this被意外delete和引用计数异常的问题。
}
private:
shared_ptr<obj> *p;
};
一个坑(以GNU版本STL为例)
先看看enable_shared_from_this
template<typename _Tp>
class enable_shared_from_this
{
protected: //在public继承后,仍对子类可见
enable_shared_from_this() noexcept { }
~enable_shared_from_this() { }
//...
public:
shared_ptr<_Tp>
shared_from_this()
{ return shared_ptr<_Tp>(this->_M_weak_this); }
//...
private:
mutable weak_ptr<_Tp> _M_weak_this; //const函数也可以修改该变量
// _M_weak_assign函数在类型T被包覆在shared_ptr的过程中会被调用
template<typename _Tp1>
void _M_weak_assign(_Tp1* __p, const __shared_count<>& __n) const noexcept
{ _M_weak_this._M_assign(__p, __n); }
};
// 其中_M_assign()是weak_ptr的父类__weak_ptr的成员函数
// _M_assign()的源码如下
void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept
{
if (use_count() == 0)
{
_M_ptr = __ptr;
_M_refcount = __refcount;
}
}
// _M_assign()的作用是为指针赋地址和引用计数(当然引用计数对weak_ptr没什么意义)
// 所以,_M_weak_assign这个会在智能指针构造时会被调用的函数,是为了为从enable_shared_from_this中继承得来的_M_weak_this赋地址!
通过上面的分析,可以得出一个结论,这样的代码是错误的:
auto p = new obj();
p->getSharedFromThis()->doSomething(); // 这里会报一个异常:bad_weak_ptr,坑
原因在于没有把obj交给shared_ptr管理起来而是直接使用了原生指针
所以导致了 _M_weak_this没有初始化!
进而在调用shared_from_this()时,把一个空weak_ptr转换为shared_ptr导致了异常。
同理,也不能将继承了enable_shared_from_this的对象创建在栈上。
这是STL在提醒我们,不要破坏智能指针的语义!
shared_from_this()不能在构造函数中被调用!
原理分析
如果捋清楚了“坑”,那么原理就很好解释了。一句话可以概括:
enable_shared_from_this