源码解析
首先先看如下简单代码,我们通过代码的顺序逐步解析
#include <iostream>
#include <memory>
using namespace std;
class C :public enable_shared_from_this<C>{
public:
C(){ std::cout<<"construct"<<endl; }
~C(){ cout<<"destruct"<<endl; }
//C*object_ptr(){ return this; }
shared_ptr<C> object_ptr(){
return shared_from_this();
}
};
int main() {
shared_ptr<C> p1(new C);
cout << p1.use_count() << endl;
shared_ptr<C> p2(
p1->object_ptr());
cout << p2.use_count() << endl;
return 0;
}
接下来,让我们随着程序代码一步步的抽丝剥茧,看清c++智能指针背后的实现原理
首先我们先看main函数第一句代码,这一行代码有两个关键,
- 奇异递归模板 基类enable_shared_from_this的构造
- shared_ptr的构造
shared_ptr<C> p1(new C);
首先,在new对象时候,先执行构造函数,由于enable_shared_from_this
是基类,cpp在构造对象时候是自底向上,所以此时子类还未开始构造,enable_shared_from_this
在此时啥也不干,执行默认构造,enable_shared_from_this
有一个成员变量weak_ptr
,weak_ptr
也进行默认构造(指针都被初始化为nullptr
)
我们首先认识一下**weak_ptr
/unique_ptr
的成员变量内存布局树结构(weak_ptr
与unique_ptr
的内存布局可以说是一样的,细节看如下)**
class enable_shared_from_this{
mutable weak_ptr<_Tp> _M_weak_this;
}
// weak_ptr是其基类__weak_ptr的包装类,所以我们直接看__weak_ptr
class __weak_ptr{
// 其实是一个指针,指向被监视变量的地址
element_type* _M_ptr; // Contained pointer.
// 这就是weak_ptr智能指针的引用计数
__weak_count<_Lp> _M_refcount; // Reference counter.
//__shared_count<_Lp> _M_refcount; // Reference counter.
};
class __shared_count{
//如下是智能指针引用计数的成员变量指针,里面放着真正的引用计数数据
_Sp_counted_base<_Lp>* _M_pi;
};
class __weak_count{
//如下是智能指针引用计数的成员变量指针,里面放着真正的引用计数数据
_Sp_counted_base<_Lp>* _M_pi;
};
class _Sp_counted_base{
// 资源被shared_ptr持有数量
_Atomic_word _M_use_count; // #shared
// 资源被weak_ptr监视数量
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
}
此时对象已经构造完毕,new返回对象地址,进入unique_ptr
的构造函数,在这里指向资源的_M_ptr
和指向引用计数的_M_refcount
被初始化
如下图所示,此图是上图中_M_refcount
指向引用计数指针初始化函数,
还记得前面的enable_shared_from_this
吗,在之前的对象构造初始化中,enable_shared_from_this
被默认初始化,现在unique_ptr
初始化完毕后在_M_enable_shared_from_this_with
函数中会检测对象是否继承了enable_shared_from_this
,如有,便在刺客进行真正的初始化
__enable_shared_from_this_base
():这里直接返回了指针_M_weak_assign
():调用weak_ptr
的\_M_assign
函数进行真正的初始化enable_shared_from_this
的weak_ptr
对象,
如此,整个shared_ptr p1(new C);执行流程便是结束了.接下来调用cout << p1.use_count() << endl;此时由于只有一个输出1,
此时有一个unique_ptr
在引用对象和enable_shared_from_this
基类的weak_ptr
在监视对象,所以use count
和weak count
数量都是1,
接下来我们再看调用shared_form_this
构造一个shared_ptr
会发生什么
可以看见,shared_form_this
实际就是拿基类enable_shared_from_this
所持有的weak_ptr
去构建一个uniqe_ptr并返回
shared_ptr<_Tp> shared_from_this() {
return shared_ptr<_Tp>(this->_M_weak_this);
}
接下来可以看见,此时是两个unique_ptr
和一个weak_ptr
内存布局图
画的一个简易的智能指针内存布局
简单总结
使用std::make_shared与new的内存区别
使用new
,资源的内存在堆上面,使用make_shared
资源在STL空间配置器(内存池)中
enable_shared_from_this的实现原理
通过c++模板编程的奇异爹递归模板,基类持有一个派生类的weak_ptr
,在shared_form_this
函数中使用weak_ptr
初始化一个unique_ptr
返回给用户使用
shared_ptr与weak_ptr
一同指向资源的地址,引用计数在堆上面,二者指向同一份引用计数(通过原子变量保证引用计数的多线程安全性),引用计数为0时候销毁,但如果有weak_ptr
在监视资源,会导致延迟销毁
tr`返回给用户使用
shared_ptr与weak_ptr
一同指向资源的地址,引用计数在堆上面,二者指向同一份引用计数(通过原子变量保证引用计数的多线程安全性),引用计数为0时候销毁,但如果有weak_ptr
在监视资源,会导致延迟销毁