智能指针

常用的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被c++11弃用

std::auto_ptr

例如auto_ptr<A> ptest(new A());
ptest.get()返回一个原始指针(注意是".")
ptest.release()只是把智能指针赋值为空,但是它原来指向的内存并没有被释放,相当于它只是释放了对资源的所有权
ptest.reset()重新绑定指向的对象,可以使用它提前释放资源
判断智能指针是否为空不能用if(ptest == NULL),应该用if(ptest.get() == NULL)

std::unique_ptr

unique_ptr是取代C++98的auto_ptr的产物,它是个独享所有权的智能指针,提供了严格意义上的所有权,包括:
1.拥有它指向的对象
2.无法进行拷贝构造,无法进行复制赋值操作.即2个unique_ptr无法指向同一个对象,但可以进行移动构造和移动赋值.
3.保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象.
unique_ptr可实现以下功能:
1.为动态申请内存提供异常安全
2.将动态申请的内存所有权传递给某函数
3.从某个函数返回动态申请内存的所有权
4.在容器中保存指针
5.auto_ptr 应该具有的功能
它和auto_ptr用法很相似,不过不能使用两个智能指针赋值,应使用std::move;而且它可直接用if(ptest==NULL)判断空指针;另外要注意,当它当做参数传递给函数时(值传递,引用不用)也要用std::move,如foo(std::move(ptest)).它还新增swap成员函数用于交换2个智能指针的值.

std::shared_ptr

从名字可看出它看被多个指针共享,调用release()释放当前资源所有权,计数减一.

struct A;
std::shared_ptr<A> p1 = std::make_shared<A>();
std::shared_ptr<A> p2(new A);

使用refcount技术实现实现的,因此内部有一个计数器(控制块,用于管理数据)和一直指针,指向数据.因此在执行std::shared_ptr p2(new A)的时候,首先会申请数据的内存,然后申请内控制块,因此是两次内存申请,而std::make_shared()则是只执行一次内存申请,将数据和控制块的申请放到一起.那么问题来了,二者会带来什么不同的影响吗?

异常安全

考虑如下代码:

void f(std::shared_ptr<Lhs> &lhs, std::shared_ptr<Rhs> &rhs){...}
f(std::shared_ptr<Lhs>(new Lhs()),
  std::shared_ptr<Rhs>(new Rhs()));

C++允许参数在计算的时候打乱顺序(不同调用约定入栈顺序就不一样),例如其中一种顺序为:new Lhs=>new Rhs=>std::shared_ptr=>std::shared_ptr
假设此时,第二步异常,那么Lhs就会没处释放,造成内存泄漏,因为指针没能马上传给std::shared_ptr.因此,一个可能的解决方法:

auto lhs = std::shared_ptr<Lhs>(new Lhs());
auto rhs = std::shared_ptr<Rhs>(new Rhs());
f(lhs, rhs);

而一个比较好的方法是使用std::make_shared:

f(std::make_shared<Lhs>(),
  std::make_shared<Rhs>());

那么,make_shared就完美了吗?

make_shared的缺点

因为make_shared只申请一次内存,因此控制块和数据块在一起;
只有当控制块中不再使用时,内存才会释放;但是weak_ptr却使得控制块一直在使用.

std::weak_ptr

weak_ptr用来协助shared_ptr工作,可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数(ref count)的变化,也没重载*和->,但可以通过lock获得一个可用的shared_ptr对象,其主要用途有2个:

  1. 用来记录对象是否存在了
  2. 用来解决shared_ptr环形依赖问题(解决shared_ptr相互引用导致的死锁问题)
    下面是存在环形依赖的代码:
class B;
class A
{
public:
    shared_ptr<B> pb_;
    ~A() {cout<<"A delete\n";}
};
class B
{
public:
    shared_ptr<A> pa_;
    ~B(){cout<<"B delete\n";}
};
 void fun()
{
    shared_ptr<B> pb(new B());
    shared_ptr<A> pa(new A());
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout<<pb.use_count()<<endl;
    cout<<pa.use_count()<<endl;
}
 int main()
{
    fun();
    return 0;
}

可以看到pa,pb相互引用,两个资源计数为2,跳出函数2个资源引用计数会减一.但二者引用计数还是1,导致资源没被释放,只要把其中一个改为weak_ptr就可以了.例如把A中的shared_ptr<B> pb_;改为weak_ptr<B> pb_;这样资源B引用计数开始就是1,pb析构B释放的同时A计数减一,pa析构计数再次减一,A也得到释放.
我们不能通过weak_ptr直接访问对象的方法,不如B中有一个方法print(),我们不能这样pa->pb_->print(),因为pb_是一个weak_ptr,应先把它转为shared_ptr.如:sahred_ptr<B> p = pa->pb_.lock(); p->print();

本文主要参考来源:https://www.cnblogs.com/TenosDoIt/p/3456704.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值