智能指针

智能指针有三类: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
void f1(){
// 使用对象初始化智能指针
shared_ptr<int> p = make_shared<int>(12);
cout << *p << " " << p.use_count() << endl;

// 使用智能指针初始化智能指针
shared_ptr<int> p2(p);
cout << *p2 << " " << p2.use_count() << endl;

// 使用内置指针初始化智能指针
int *p3 = new int(193);
shared_ptr<int> p4(p3);
cout << *p4 << " " << p4.use_count() << endl;
}

运行结果

1
2
3
12 1
12 2
193 1

reset和use_count的使用

调用p.reset(q)时, 若是p是唯一一个指向其对象的shared_ptr,则reset会释放此对象.若传递了可选的参数内置指针q, 会令p指向q所指的内存空间, 否则将p置空.reset会更新p的引用计数.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void f2(){
shared_ptr<int> p4(new int(28));
cout << "*p4: " << *p4 << " p4.use_count(): " << p4.use_count() << endl;

shared_ptr<int> p5(new int(109));
cout << "*p5:" << *p5 << " p5.use_count: " << p5.use_count() << endl;

// 调用智能指针类的赋值运算符函数
p4 = p5;
cout << "*p4: " << *p4 << " p4.use_count(): " << p4.use_count() << endl;

// p5不是唯一一个指向其所指对象的shared_ptr,因此,重新制作一份该对象的副本并修改该对象的值
if (!p5.unique()) {
p5.reset(new int(*p5));
*p5 += 100;
}
cout << "*p5:" << *p5 << " p5.use_count: " << p5.use_count() << endl;
}

运行结果

1
2
3
4
*p4: 28 p4.use_count(): 1
*p5:109 p5.use_count: 1
*p4: 109 p4.use_count(): 2
*p5:209 p5.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>

ptr

当_pb是weak_ptr<B>类型时,对于B类型对象现在引用计数为1,因为有一个weak_ptr<B>和一个shared_ptr<\B>指向它。对于A类型对象,引用计数仍为2。当离开局部作用域时,pb的引用计数为1,因此会调用B类型对象的析构函数,这会导致A类型对象的引用计数减1,同时pa析构时A类型对象的引用计数又减1,此时A类型对象的引用计数为0,于是调用析构函数,释放资源。