【C++】一文搞懂智能指针

智能指针基本概念

头文件memory

智能指针的->运算符重载

不带引用计数的智能指针

为什么智能指针要区分带引用计数或不带引用计数呢?

因为多个智能指针指向同一份资源会引起资源的重复释放,即浅拷贝资源释放的问题,所以有的不带引用计数,但是只能有一个智能指针指向该份资源,有的带引用计数

auto_ptr

auto_ptr怎么解决浅拷贝资源多份释放的问题?

答:永远让最后一个auto_ptr指向该份资源,前边的auto_ptr被置为nullptr,图示如下:


所以auto有很多缺点:

  • 不推荐使用auto_ptr,并且在容器中最好不要用auto_ptr,因为如果容器里都是auto_ptr,如果容器要拷贝,那么之前的容器里的auto_ptr就全被置为nullptr了,说的绝对点就是:auto_ptr 不能被用在 stl 标准容器中
  • 因为auto_ptr的所有权独有,所以要防止两个auto_ptr拥有同一个对象

由于auto_ptr的种种弊端,C11舍弃掉了auto_ptr

scoped_ptr(boost库中的)

scope怎么解决浅拷贝资源多份释放的问题?

答:scope直接把拷贝构造和赋值操作给删除/禁掉了,也就是语法上不让你多个指针指向同一份堆内存,不让你用一个scoped_ptr去构造另一个scoped_ptr,scope_ptr没有提供带右值引用的拷贝构造

scoped_ptr使用的比较少,不带引用计数的智能指针推荐使用unique_ptr

unique_ptr

不带引用计数的智能指针推荐使用unique_ptr

unique_ptr只让一个智能指针管理资源,unique_ptr也禁掉了拷贝构造和赋值,但是提供了带右值引用的拷贝构造和赋值重载,所以涉及到右值的拷贝构造都是可以用unique_ptr的,比如用std::move移动语义将一个指针的资源转移到新指针、作为函数返回值类型,unique_ptr相当于独占资源

unique_ptr<int> p1(new int);
unique_ptr<int> p2(p1);  //error
//但是可以这样:
unique_ptr<int> p2(std::move(p1));  //ok

template<typename T>
unique_ptr<T> getSmartPtr()
{
    unique_ptr<T> ptr(new T());
    return ptr;  //ok
}

unique_ptr<int> up = getSmartPtr<int>(); //ok

unique_ptr相比于auto_ptr的好处:

  • unique_ptr在写的时候,如果想拷贝构造成功,用户是一定要写std::move的,用意是很明显的(资源转移)
  • 而auto_ptr没有禁掉拷贝构造,却底层实现了资源的转移,用户正常使用ptr2(ptr1);,却已经把ptr1的资源转移给ptr2了,就很危险

带引用计数的智能指针shared_ptr和weak_ptr

带引用计数:多个智能指针可以管理同一个资源

带引用计数:给每一个对象资源,匹配一个引用计数

智能指针 =》引用资源的时候 =》引用计数+1

智能指针 =》不使用资源的时候 =》引用计数-1 =》!=0 不释放资源,为0释放资源

库里面的shared_ptr和weak_ptr引用计数的加减是线程安全的,因为用atomic定义了引用计数

shared_ptr是强智能指针,可以改变资源的引用计数

循环引用问题,造成new出来的资源无法释放,资源泄露

class B;
class A
{
public:
    A() { cout<<"A()"<<endl; }
    ~A() { cout<<"~A()"<<endl; }
    shared_ptr<B> ptrb;
};

class B
{
public:
    B() { cout<<"B()"<<endl; }
    ~B() { cout<<"~B()"<<endl; }
    shared_ptr<A> ptra;
};

int main()
{
    shared_ptr<A> pa(new A());
    shared_ptr<B> pb(new B());
    
    pa->ptrb = pb;
    pb->ptra = pa;
    
    cout<<pa.use_count() <<endl;  //打印引用计数
    cout<<pb.use_count() <<endl; 
}

如何解决循环引用问题?

  • 定义对象的时候,用强智能指针;引用对象的地方,使用弱智能指针
  • weak_ptr之所以可以打破循环引用,是因为:将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QkJ45qTP-1650185449052)(img/9C++%EF%BC%9A%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88.img/image-20210301132417281.png)]

weak_ptr是弱智能指针,不会改变资源的引用计数

weak_ptr =》(观察)shared_ptr =>(管理) 资源(内存)

注意:weak_ptr只是一个观察者,它并不能直接操纵资源,没有重载->和*运算符,所以不能->这样输出,要用lock返回shared_ptr类型才能用->

强弱智能指针解决的另一问题:多线程访问共享对象的线程安全问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l14US7XX-1650185449054)(img/9C++%EF%BC%9A%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88.img/image-20210301150137883.png)]

自定义删除器

智能指针的删除器:deletor,对于不同的资源有不同的资源释放方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g6IZCHRl-1650185449055)(img/9C++%EF%BC%9A%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88.img/image-20210301154723647.png)]

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值