智能指针属于C++中的重中之重了,趁着最近有点时间好好总结一下。
为什么要使用智能指针?
智能指针最大的作用就是:管理在堆上申请的内存,避免内存泄露。
因为在我们在写代码的时候,如果在堆上申请了内存,离开该作用于时却忘记释放这块内存,那么这块申请的内存只要进程一直在运行,我们就永远无法使用它,从而造成了内存泄露。
智能指针运用了RAII的机制,让我们管理内存变得简单。
那么什么又是RAII机制呢?
简单的说就是:资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配,用时由对象的析构函数完成资源的释放。在这种要求下,只要对象能够正确的析构,就不会出现资源泄露的问题。
auto_ptr
auto_ptr 出现在 C++98 的方案中,现已被C++11抛弃。
被抛弃的原因: auto_ptr 负责维护一个对象指针,在自身析构的时候一定会通过 delete 方式释放非空指针。因此auto_ptr不能管理对象数组指针。为了避免重复释放,在 auto_ptr 实例之间赋值时,会转移托管指针的控制权,即在赋值完成后,托管指针变为nullptr。
| 1 | auto_ptr<string> p1(new string("hello")); | 
上述代码可以正常的编译,但是会出现潜在的问题因为p1已经把所有权转交给p2,当我们再次访问p1时候会报错。

所以auto_ptr的缺点是:潜在着内存崩溃的问题。
unique_ptr
unique_ptr简单的说是auto_ptr的升级版。它实现了严格拥有的概念,保证了同一时间内只有一个智能指针可以指向该对象。
| 1 | unique_ptr<string> p1(new string("hello")); | 
编译器认为 p2 = p1是非法的,从而避免了p1被转移后指向nullptr的问题。因此unique_ptr较之auto_ptr更安全。
另外unique_ptr还有更聪明的地方: 当程序试图将unique_ptr赋值给另一个时:
1. 如果源`unique_ptr`将要存在一段时间,那么编译器将禁止这么做。(上面例子说明了这个问题)
   2. 如果源`unique_ptr`是**临时的右值**,那么编译器将允许这么做。(见下)| 1 | unique_ptr<string> p3; | 
unique_ptr的实现
| 1 | template<typename T> | 
shared_ptr
shared_ptr实现了共享式拥有的概念。多个智能指针可以指向相同对象,该对象可以和其相关资源会在“最后一个引用被销毁”的时候释放。(是不是有点像inode的引用计数,当inode引用计数为0的时候才会把inode指向的data删除)。
「share_ptr 内部的应用计数是线程安全的,但对象的读取需要加锁」
share_ptr不仅可以通过new来构造,还可以通过传入auto_ptr,unique_ptr,weak_ptr来构造。
share_ptr解决了auto_ptr在对象所有权上的局限性(独占式),在使用引用计数的机制上提供了可以共享所有权的智能指针。
shared_ptr的实现
| 1 | template<typename T> | 
shared_ptr的缺陷 从上面的描述看来
shared_ptr已经非常的完美了,但是真的是这样吗?
我们拿个双向链表作为例子来说明shared_ptr造成循环引用
| 1 | 
 | 
由上我们可以看到ptr_1->_next = ptr_2; ptr_l1的引用计数增加1,变为2。ptr_l2同理。

当我们要销毁ptr_l1时,我们必须使得它的引用计数在定义其的定义域内为1,因为离开作用域时候才会正常减1,使得其被销毁。
所以我们只能把ptr_l1->next销毁,但是ptr_l1->next是ptr_l2,它要被销毁必须使得ptr_l2->prev被销毁掉,所以就形成了一个相互僵持的状态(是不是很类似于死锁),导致两者都不能成功被销毁,从而致使内存泄露。
weak_ptr
weak_ptr的作用就是避免shared_ptr陷入循环引用这种尴尬的局面。
weak_ptr是一种不控制对象生命周期的智能指针,它指向一个shared_ptr管理的对象。
weak_ptr只是提供了管理对象的一个访问手段,从另一方面来说它的指向并不会是的shared_ptr对象的引用计数加一。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr可以相互转换,shared_ptr也可以直接赋值给它,它也可以通过调用lock()来获得shared_ptr。

上面那种尴尬的局面可以把代码改成:
| 1 | 
 |