通过源码抽丝剥茧理解enable_shared_form_this/shared_ptr/weak_ptr智能指针实现原理

源码解析

首先先看如下简单代码,我们通过代码的顺序逐步解析

#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函数第一句代码,这一行代码有两个关键,

  1. 奇异递归模板 基类enable_shared_from_this的构造
  2. 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)

image-20240623150549041

我们首先认识一下**weak_ptr/unique_ptr的成员变量内存布局树结构(weak_ptrunique_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被初始化

image-20240623152301321

如下图所示,此图是上图中_M_refcount指向引用计数指针初始化函数,

image-20240623153113331

还记得前面的enable_shared_from_this吗,在之前的对象构造初始化中,enable_shared_from_this被默认初始化,现在unique_ptr初始化完毕后在_M_enable_shared_from_this_with函数中会检测对象是否继承了enable_shared_from_this,如有,便在刺客进行真正的初始化

  1. __enable_shared_from_this_base():这里直接返回了指针
  2. _M_weak_assign():调用weak_ptr\_M_assign函数进行真正的初始化enable_shared_from_thisweak_ptr对象,

image-20240623153624491

image-20240623153724299

image-20240623154532042

如此,整个shared_ptr p1(new C);执行流程便是结束了.接下来调用cout << p1.use_count() << endl;此时由于只有一个输出1,

此时有一个unique_ptr在引用对象和enable_shared_from_this基类的weak_ptr在监视对象,所以use countweak count数量都是1,

image-20240623155304716

接下来我们再看调用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

image-20240623160514744

内存布局图

画的一个简易的智能指针内存布局

image-20240623161149740

简单总结

使用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在监视资源,会导致延迟销毁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@马云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值