C++知识点:智能指针

以前面试虾皮时问到了智能指针相关知识点,当时答的很没有条理,这里整理一下权当笔记。面试

根据《C++ Primer Plus》中的解释,智能指针是行为相似于指针的模板对象。当函数分配堆内存时,必定要记得函数结束前回收内存。可是若是函数异常停止,本地变量都将从栈内存中删除,以下例,此时ps指针占据的内存也会被释放,从而致使资源泄露。所以若是指针ps有一个析构函数,在ps过时时自动释放它管理的内存,这就是智能指针的思想。数组


auto_ptr安全

auto_ptr目前已被抛弃。参考下例,当咱们对auto_ptr类型的智能指针进行赋值时,原来的对象ps被接管成为空指针,后续操做ps就会致使core dump。就算不操做ps,由于两个指针指向同一对象,会致使该对象被删除两次。微信


auto_ptr被explicit关键字修饰,意味着智能指针只能被显示转换(也就是不能被隐式强制转换)。判断一个智能指针是否为空不能使用if(ps== NULL),而应该使用if(ps.get() == NULL),由于ps是指针对象,不是指针。函数

unique_ptrui

unique_ptr 是一个独享全部权的智能指针,它提供了严格意义上的全部权,包括:spa

一、拥有它指向的对象;.net

二、没法进行复制构造和复制赋值操做。即没法使两个unique_ptr指向同一个对象。可是能够进行移动构造(std::move)和移动赋值操做,也就是浅拷贝;线程

三、保存指向某个对象的指针,当它自己被删除释放的时候,会使用给定的删除器释放它指向的对象;3d

仍是上面的例子,这时候编译器会认为赋值语句非法,避免了ps再也不指向有效数据的问题,所以unique_ptr比auto_ptr更安全(编译阶段错误比运行阶段程序crash更安全)。


不过对于返回值为unique_ptr类型的函数,能够用另外一个unique_ptr智能指针接收函数返回值。由于函数返回的临时变量很快被销毁,程序没法使用它来访问无效的数据。

unique_ptr还有另外一个优势。auto_ptr使用delete而不是delete[],所以只能与new搭配使用;而unique_ptr使用delete[],能够与new[]搭配使用,所以能够用于数组。

shared_ptr

shared_ptr使用计数机制来代表资源被几个指针共享。能够经过成员函数use_count()来查看资源的全部者个数。除了能够经过new来构造,还能够经过传入auto_ptr, unique_ptr,weak_ptr来构造。


咱们来看一下shared_ptr智能指针的赋值操做,p2 = p1;p2管理的对象计数值减1(但不必定释放对象),p1管理的对象计数值加1。若是p2管理的对象计数值为0,则自动释放管理的对象。此时无论p2以前是否初始化(未赋值),p1与p2管理相同的对象(也就是原来p1管理的对象),所以赋值后p1与p2计数一致,获得:

p1.use_count()==p2.use_count();

有个问题,p2指向p2管理的对象以后,它原来管理的对象怎么办呢?若是资源A计数值为0(说明已经没有别的指针指向它了),资源A会被自动释放;若是计数值不为0(说明还有其余指针管理着它),那么就不用管。


weak_ptr

weak_ptr是用来解决shared_ptr相互引用时的死锁问题。若是两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能将为0,这时资源永远不会释放。而weak_ptr是对对象的一种弱引用,不会增长对象的引用计数。此外weak_ptr和shared_ptr能够相互转化,shared_ptr能够直接赋值给它,它能够经过调用lock()函数来得到shared_ptr。


上面这个例子,类A和类B分别持有对方的智能指针,对象pa和pb相互引用后各自资源的引用计数都为2。当跳出函数时,智能指针pa、pb前后析构,引用计数减一,可是此时二者计数仍是为1,致使函数结束时资源没有被释放。此时若是咱们把类A里面的shared_ptr<B> pb_改成weak_ptr<B>pb_,这样资源B的引用计数就只有1,当pb析构时,B的计数变为0,B获得释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A获得释放。

注意的是咱们不能经过weak_ptr直接访问对象的方法,好比B对象中有一个方法print(),咱们不能经过pb->print(); 由于pb是一个weak_ptr,应该先把它转化为shared_ptr,即:shared_ptr<B> p =pb.lock();    p->print();

对于函数中的多个变量,最早申请的对象最后释放。说到函数栈空间,咱们说函数栈释放,实际是指寄存器pc地址返回而已,栈空间的数据仍是存在的,以下例:


调用fun1返回以后,虽然栈变量a被释放,可是fun2进入函数调用立刻入栈,由于使用的是同一块栈空间,所以当fun2内的临时变量未初始化时,内存里面的脏数据就是fun1残留下来的,上面的示例中fun2的变量b若是不自行初始化,那初始值就是1。

智能指针shared_ptr虽然能解决资源自动回收,但并非线程安全的,通俗的讲就是修改指向资源的指针和修改计数值是两步操做,须要mutex保护。

另外若是是将原始指针强制转换未智能指针的话,由于原始资源(例子中未widget)已经分配好了,那么shared_ptr只能未引用计数再单独分配控制块。

 auto p = new widget(); shared_ptr sp1{ p }; ...

若是选择使用 make_shared 的话,原始资源和引用计数的分配, 能够一次性完成,减小了内存分配的次数。

最后,祝你们双十一多买多卖。

本文分享自微信公众号 - 机械猿(on_ourway)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索