智能指针有三类: shared_ptr, unique_ptr, weak_ptr.
其中shared_ptr可以和其他shared_ptr共享内存,指向同一块内存区域,引用计数用于记录指向该内存的shared_ptr个数.
unique_ptr只能独占内存.任意时刻,都只有一个unique_ptr指向一个给定对象.当unique_ptr被销毁时, 它所指向的对象也会被销毁.
weak_ptr是一种弱引用,指向shared_ptr所管理的对象.
shared_ptr
- 初始化
shared_ptr有三种初始化方式:- 使用make_shared<T>(obj)初始化
- 使用同类型内置指针对shared_ptr进行初始化
- 使用另一个智能指针进行初始化
- reset
将一个新的内置指针赋予shared_ptr - use_count
引用计数
默认情况下,一个用来初始化智能指针的内置指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象.我们也可以将智能指针绑定一个指向其他类型对象的指针上,但是此时必须提供自己的操作来代替delete.这个自己的操作就是删除器函数,即,我们必须定义一个自己的函数作为新的删除器函数.
1 | void f1(){ |
运行结果
1 | 12 1 |
reset和use_count的使用
调用p.reset(q)时, 若是p是唯一一个指向其对象的shared_ptr,则reset会释放此对象.若传递了可选的参数内置指针q, 会令p指向q所指的内存空间, 否则将p置空.reset会更新p的引用计数.
1 | void f2(){ |
运行结果
1 | *p4: 28 p4.use_count(): 1 |
unique_ptr
weak_ptr
weak_ptr是一种不控制对象生命周期的智能指针,它指向一个share_ptr管理的对象,进行该对象的内存管理的是强引用的share_ptr,weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr设计的目的是为配合share_ptr而引入的一种智能指针来协助shared_ptr工作,它只可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起对象引用计数的增加和减少。weak_ptr使用来解决shared_ptr相互引用时的死锁问题,如果两个shared_ptr相互引用,那么这两个指针的引用计数永远都不可能下降为0,资源永远不能释放。weak_ptr是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
假设现在有两个类A和B,在局部作用域中定义一个A类型的指针pa和一个B类型的指针pb。如图所示,A类型对象中有一个成员智能指针指向B类型对象,而B类型对象中有一个A类型成员智能指针指向A类型对象。A类型智能指针pa指向A类型对象,B类型智能指针pb指向B类型对象。则有两个智能指针指向A类型对象,所以A类型对象的引用计数为2,同理B类型对象的引用计数也为2。当离开局部作用域时,pa和pb析构时,二者所指向的资源即A类型对象和B类型对象的引用计数减1,但是两者的引用计数都为1,所以,A和B的析构函数没有被调用,因此,资源未被释放。因此造成了资源泄露。解决方法是将类型A中的成员指针改为weak_ptr<B>。
当_pb是weak_ptr<B>类型时,对于B类型对象现在引用计数为1,因为有一个weak_ptr<B>和一个shared_ptr<\B>指向它。对于A类型对象,引用计数仍为2。当离开局部作用域时,pb的引用计数为1,因此会调用B类型对象的析构函数,这会导致A类型对象的引用计数减1,同时pa析构时A类型对象的引用计数又减1,此时A类型对象的引用计数为0,于是调用析构函数,释放资源。