C++11智能指针shared_ptr介绍和辅助函数

本文详细介绍了C++11中的智能指针std::shared_ptr,包括其共享所有权的概念、引用计数机制、以及reset、swap和赋值等关键操作的实现原理。std::shared_ptr通过引用计数管理资源,确保在适当的时候自动释放,避免内存泄漏。文章还展示了reset、swap和赋值函数的具体代码实现,强调了使用make_shared的效率和安全性优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、shared_ptr介绍

做出一个像Java中垃圾回收器,并且可以运用到所有资源,heap内存和系统资源都可以使用的系统。std::shared_ptr就是C++11推出的解决方案

shared_ptr实现了共享所有权(shared ownership)方式来管理资源对象,这意味没有一个特定的std::shared_ptr拥有资源对象。相反,这些指向同一个资源对象的std::shared_ptr相互协作来确保该资源对象在不需要的时候被析构

shared_ptr特点:

  • 基于共享所有权模式:多个指针能够同时指向同一个资源
  • 基于共享所有权,使用引用计数控制块管理资源对象的生命期
  • 提供带左值引用的拷贝构造函数和赋值重载函数;提供带右值引用的移动构造和移动赋值函数
  • 为了实现单个对象和一组对象的管理,添加了删除器类型
  • 在容器保存shared_ptr对象是安全
  • shared_ptr重载了operator-> 和operator*运算符,因此它可以像普通指针一样使用

引用计数的作用:

  • 当旧的shared_ptr对象与资源对象的地址关联时,则在其构造函数中,将与此资源对象关联的引用计数增加1
  • 当任何shared_ptr对象超出作用域时,则在其析构函数中,将与资源对象关联的引用计数Uses_减1。如果引用计数Uses_变为0,则表示没有其他shared_ptr对象与此资源对象关联,在这种情况下,它使用deleter删除器释放该资源对象
  • 构造shared_ptr对象管理一个未被管理的对象时,Uses_和Weaks_都是1,只有当Uses_减为0时,才会将Weaks_减1
int main() {
	shared_ptr<int> p1(new int(10));    // 构造p1后,p1._Rep._Uses = 1,p1._Rep._Weaks = 1
	{
		shared_ptr<int> p2(p1);         // 拷贝构造p2后,p2和p1使用同一个引用计数对象,p1._Rep._Uses = 2,p1._Rep._Weaks = 1
		cout << p1.use_count() << endl; // 2
	}                                   // p2析构后,p1._Rep._Uses = 1,p1._Rep._Weaks = 1
	cout << p1.use_count() << endl;     // 1
	return 0;
}                                       // p2析构后,p1._Rep._Uses = 0,p1._Rep._Weaks = 0

注:Int对象源码在C++11智能指针unique_ptr剖析

对于没使用make_shared构造shared_ptr时,资源空间和引用计数对象空间是分两次申请的,地址不连续。 当_Uses为0时,即可释放被管理的资源空间。但只有当引用计数对象的_Uses和_Weaks同时为0时,才会释放引用计数控制块

最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配对象空间和引用计数块(地址是连续的),最后返回指向此对象的share_ptr实例。如果你不想使用make_shared,也可以先明确new出一个对象,然后把其原始指针传递给share_ptr的构造函数

二、辅助函数

  • get:返回指向被管理对象的指针,不建议使用
  • reset:替换被管理的对象,释放原来管理的资源,管理新的资源
  • release:shared_ptr不提供解除智能指针和资源管理关系的函数
  • swap:交换被管理对象的所有权
  • operator bool() const:检查是否有关联的被管理对象
  • 重载operator*,operator->,访问被管理的对象
  • bool unique() const:检查所管理的对象是否仅由当前shared_ptr管理,在C++20中被移除

1. reset函数

int main() {
	shared_ptr<Int> p1 = make_shared<Int>(10);
	p1.reset(new Int(20));      // 先把new Int(20)返回的Int*传入shared_ptr的构造函数,然后执行swap
	return 0;
}

reset源码如下:

template <class _Ux>
void reset(_Ux* _Px) { // release, take ownership of _Px
    shared_ptr(_Px).swap(*this);
}

2. swap函数

	shared_ptr<Int> p1 = make_shared<Int>(10);
	shared_ptr<Int> p2 = make_shared<Int>(20);
	p1.swap(p2);     // p1._Ptr <=> p2._Ptr       p1._Rep <=> p2._Rep
void shared_ptr::swap(shared_ptr& _Other) noexcept {
    this->_Swap(_Other);
}

void _Ptr_base::_Swap(_Ptr_base& _Right) noexcept { // swap pointers
    _STD swap(_Ptr, _Right._Ptr);       // 交换资源对象
    _STD swap(_Rep, _Right._Rep);       // 交换引用计数对象
}

shared_ptr调用swap函数,其实就是使用std::swap交换了资源对象_Ptr和引用计数对象_Rep

3. 赋值函数

shared_ptr<Int> p1 = make_shared<Int>(10);
shared_ptr<Int> p2(new Int(20));
p1 = p2;                    // p1要管理新的对象,就要释放原来管理的对象Int(10),p1和p2同时管理Int(20)

带左值引用参数的赋值重载函数源码如下:

shared_ptr& operator=(const shared_ptr& _Right) noexcept {
    shared_ptr(_Right).swap(*this);
    return *this;
}

void shared_ptr::swap(shared_ptr& _Other) noexcept {
    this->_Swap(_Other);
}

shared_ptr(const shared_ptr& _Other) noexcept { // construct shared_ptr object that owns same resource as _Other
    this->_Copy_construct_from(_Other);
}

template <class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {
    // implement shared_ptr's (converting) copy ctor
    _Other._Incref();          // 引用计数对象的_Uses + 1

    _Ptr = _Other._Ptr;        // 资源对象指针
    _Rep = _Other._Rep;        // 引用计数对象指针
}

在operator=里,this是指向Int(10),_Right是Int(20),用一个临时的shared_ptr管理Int(20)。swap执行完后,this是指向Int(20),_Right是Int(20),operator=结束时,临时的shared_ptr需要析构,于是释放了Int(10),这样就达到了让p1和p2同时管理Int(20)的目的

shared_ptr<Int> p1 = make_shared<Int>(10);
shared_ptr<Int> p2(new Int(20));
p1 = std::move(p2);         // p2被置空,p1管理Int(20),p1原来管理的Int(10)被释放

带右值引用参数的赋值重载函数源码如下:

shared_ptr& operator=(shared_ptr&& _Right) noexcept { // take resource from _Right
    shared_ptr(_STD move(_Right)).swap(*this);
    return *this;
}

shared_ptr(shared_ptr&& _Right) noexcept { // construct shared_ptr object that takes resource from _Right
    this->_Move_construct_from(_STD move(_Right));
}

template <class _Ty2>
void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {
    // implement shared_ptr's (converting) move ctor and weak_ptr's move ctor
    _Ptr = _Right._Ptr;
    _Rep = _Right._Rep;

    _Right._Ptr = nullptr;
    _Right._Rep = nullptr;
}

void swap(shared_ptr& _Other) noexcept {
    this->_Swap(_Other);
}

在带右值引用参数的operator=里,生成临时的shared_ptr时会调用带右值引用参数的shared_ptr构造函数,此时_Right管理Int(20),然后就会执行_Move_construct_from函数,这个函数会把_Right的资源(即Int(20))转移到临时的shared_ptr对象

然后在operator=里,用临时的shared_ptr对象(管理的Int(20))和this(管理Int(10))进行交换,交换完成,临时对象析构,Int(20)空间释放

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bugcoder-9905

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

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

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

打赏作者

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

抵扣说明:

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

余额充值