shared_ptr
线程安全性
shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。根据文档,shared_ptr 的线程安全级别和内建类型、标准库容器、string 一样,即:
- 一个 shared_ptr 实体可被多个线程同时读取;
- 两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
- 要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。
用法
12345678shared_ptr<int> sp(new int(10)); //一个指向整数的shared_ptrassert(sp.unique()); //现在shared_ptr是指针的唯一持有者shared_ptr<int> sp2 = sp; //第二个shared_ptr,拷贝构造函数assert(sp == sp2 && sp.use_count() == 2); //两个shared_ptr相等,指向同一个对象,引用计数为2*sp2 = 100; //使用解引用操作符修改被指对象assert(*sp == 100); //另一个shared_ptr也同时被修改sp.reset(); //停止shared_ptr的使用assert(!sp); //sp不再持有任何指针(空指针)
提示:shared_ptr可作为容器的元素,重载了比较和拷贝操作符,auto_ptr不可以
使用陷阱
意外延长声明周期
shared_ptr是强引用,只要有一个指向对象的shared_ptr存在,该对象就不会析构.
要注意bind函数,bind会把实参拷贝一份,意外延长对象生命周期
函数参数
我们用const引用来传递类型为shared_ptr的函数参数
析构所在的线程
对象的析构是同步的,当最后一个指向x的shared_ptr离开其作用域的时候,x会同时在同一个线程析构,这个线程不一定是对象诞生的线程,如果析构比较耗时,并且在关键线程中析构,那么会拖累线程的速度,
解决办法是我们可以单独开一个线程专门用来析构,通过一个BlockingQueue
注:shared_ptr
weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况.
用法:
- weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
- 使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。
- weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr.
获得this的shared_ptr
weak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this指针,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr使外界使用。
这个解决方案被实现为一个惯用法,在头文件
使用的时候只需要让想被shared_ptr管理的类从它继承即可,成员函数shared_from_this()会返回this的shared_ptr
使用示例:
weak_ptr还有个用处就是解决循环引用造成的内存泄漏,举个例子:
由于A释放时引用计数大于1,所以不析构,导致B释放时引用计数也大于1,也不析构,造成内存泄漏,解决办法是将一方改成weak_ptr,但是必须要程序员意识到这里会出现循环引用.