什么是智能指针
智能指针是自动帮你管理内存、防止内存泄漏的包装类,是现代 C++ 用来代替裸指针的工具,解决了裸指针在某些地方的痛点。比如:
1.忘记 delete ,导致内存泄漏。
2.提前 delete ,后面还要继续用,导致悬空指针,野指针。
3.异常抛出时,函数提前退出,delete 永远执行不到。
RAII
要了解智能指针,我们先从它的设计思路开始。智能指针的设计思路是 RAII (Resource Acquisition Is Initialization),这是一种 "资源获取即初始化" 的编程思想,是所有智能指针的底层原理,也是现代 C++ 管理资源的核心范式。
RAII 的核心逻辑是:把资源的生命周期,和一个栈对象的生命周期绑定在一起 。RAII 在获取资源时把资源委托给一个对象,接着控制对资源的访问,资源在对象生命周期内始终保持有效,最后在对象析构的时候释放资源,这样保障了资源的正常释放,避免资源泄漏问题。
C++标准库智能指针的使用
使用智能指针需要包含头文件 <memory>。
C++98:
智能指针在 C++98 就已经出现,但效果不尽人意。
auto_ptr:auto_ptr是 C++98 就出现的智能指针,但它的设计十分糟糕,属于 C++ 的一次失败的尝试。它的逻辑是:拷贝时,把拷贝对象的资源的管理权转移给拷贝对象,旧的对象会被置空。但这种置空是不会显示表现出来的,让人以为旧指针能用,如果此时访问就会报错。
C++11:
之前 C++98 搞出来的智能指针太糟糕,很多公司明令禁止使用。C++ 委员会也知道只是一个非常差劲的设计,于是在 C++11 移动语义出现后,迅速抛弃了 auto_ptr,搞出了几个新的智能指针。
unique_ptr: unique_ptr 不支持拷贝,只支持移动。unique_ptr 也会把旧的指针置空,但它的移动是显示的,也就是说你在使用它时你应该自己清楚旧指针会被置空,如果你还去访问旧指针就是你自己的问题了。在不需要拷贝的场景建议使用它。

shared_ptr :shared_ptr 支持拷贝,也支持移动。它的底层是使用引用计数的方式实现的。shared_ptr 除了支持用指向资源的指针进行构造,还支持用 make_shared 用初始化资源对象的值直接构造。
weak_ptr: weak_ptr 完全不同于以上指针,它不支持 RAII ,这也意味着不能用它直接管理资源。weak_ptr 的产生是为了解决循环引用的问题,这个后面细讲。
智能指针析构时默认进行 delete 释放资源,所以如果不是 new 出来的资源,交给智能指针管理会崩溃。智能指针支持在构造时给一个删除器,在它调用析构时会调用删除器去释放资源,这解决了不同 delete 的需求。
循环引用
循环引用问题出现在 shared_ptr 上,它会导致资源没有得到释放,内存泄漏,想要解决这个问题,需要用到 weak_ptr 。
先看例子:现在有 n1 和 n2 两个 shared_ptr:

假如 n1 的资源中又有一个shared_ptr 指针 _next 指向 n2,n2 的资源中又有一个shared_ptr 指针 _prev 指向 n1。注意此时计数器的变化:

当我们析构 n1 和 n2 时, 按理说资源应该被释放,但是 share_ptr 的资源释放是看的计数器释放为空,所以此时资源不会被释放:

这很显然并不符合实际需求,会导致内存泄漏。
weak_ptr 可以很好地解决这个问题。前面说过:weak_ptr 不支持 RAII 。它不支持绑定资源,只支持绑定 shared_ptr ,所以它不会增加 shared_ptr 的引用计数,我们将上面的 _next 和 _prev 指针设置为 weak_ptr 指针,就可以解决循环引用问题。

("来源:cplusplus.com")
weak_ptr 没有重载 operator* 和 operator-> 等,因为它不支持绑定资源,所以用它去访问资源是危险的,但它支持 expired 检查指向的资源是否过期,use_count 也可以获取 shared_ptr 的引用计数。weak_ptr 想访问资源时,可以调用 lock 返回一个管理资源的 shared_ptr ,如果资源已经被释放,返回的 shared_ptr 是一个空指针,如果没有释放,则通过返回的 shared_ptr 访问资源就行。