一、智能指针
auto_ptr:拷贝时转移指针的所有权
1 | template <typename T> |
上述代码中这种惯用法保证了强异常安全性:赋值分为拷贝构造和交换两步,异常只可能在第一步发生;而第一步如果发生异常的话,this 对象完全不受任何影响。无论拷贝构造成功与否,结果只有赋值成功和赋值没有效果两种状态,而不会发生因为赋值破坏了当前对象这种场景。
不过 auto_ptr 将它传递给另一个对象之后,就不再拥有这个对象了。
unique_ptr:移动指针
1 | template <typename T> |
必须以右值的形式传入,明确告诉用户对象已经转移
shared_ptr
通过引用计数的方式,多个智能指针可以拥有一个对象。存在循环引用的问题
1 | template <typename T> |
没有调用析构函数,导致内部内存泄露
循环引用:shared_ptr 是一组指针指向一个实例,有几个 shared_ptr 指向这个类,那么这个类就有几次引用。如下图
weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。
初始化方式
- 通过shared_ptr直接初始化,也可以通过隐式转换来构造;
- 允许移动构造,也允许拷贝构造。
需要注意,weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
- 如何判断weak_ptr指向对象是否存在?
既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,就不能使用weak_ptr直接访问对象。那么如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr(引用计数会增1),否则返回一个空shared_ptr。weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。
由于weak_ptr并没有重载operator ->和operator *操作符,因此不可直接通过weak_ptr使用对象,同时也没有提供get函数直接获取裸指针。典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
二、线程安全
智能指针包括一个实际数据指针和一个引用计数指针,这两个操作不是一个指令可以完成的。因此多线程情况下是有问题的
根据boost官方文档 shared_ptr_thread_safety有如下结论:
- 同一个shared_ptr被多个线程读,是线程安全的;
- 同一个shared_ptr被多个线程写、或被多个线程读写,不是线程安全的;(析构算写操作)
- 共享引用计数的不同的shared_ptr被多个线程写,是线程安全的。