参考自《effective morden c++》
裸指针的缺点
- 无法知道指向的是单个对象还是一个数组.
- 无法知道使用完之后是否需要析构,即是否拥有指向的对象.
- 无法知道应当如何析构(使用 delete 还是专门的函数)
- 无法知道单个析构还是数组析构.
- 不能保证只析构一次。少了会导致内存泄漏,多了是UB.
- 无法检查指针空悬.
std::unique_ptr
- 足够小、足够快,几乎和裸指针相同.
- 是一个只移类型(不可复制),当其析构时,内部的对象也会被析构。
- 可以自定义删除器,会略微增大尺寸.
- 自定义删除器的类型需要加入到模板参数中
- 不建议用其存储数组,可以使用 vector, array 等.
- 可以方便地转为 shared_ptr.
std::shared_ptr
- 尺寸是裸指针的两倍,因为内部既包含一个资源指针,又包含一个控制块的指针.
- 引用计数是原子性的.
- 自定义删除器不需要加入到模板参数.
- 控制块包括:引用计数、弱计数、其他数据(自定义删除器等)
- 不要用保存好的裸指针来创建 shared_ptr,直接从 new 创建。因为会创建一个新控制块.
- 由上,从 this 创建指针也是很危险的,可以让类 T 继承
std::enable_shared_from_this<T>
,然后使用 shared_from_this 函数创建.
这里的设计模式叫 奇异递归模板模式. - 不要用它保存数组
std::wake_ptr
- 用于 sp 的辅助,不能直接获取指向的资源.
- expired() 判断是否失效.
- 如果需要线程安全地判断是否失效并且没失效时返回一个 shared_ptr,可以使用
- lock(),如果失效返回的 sp 为空
- 直接创建,如果失效会返回异常.
- 用途:缓存观察是否失效,观察者模式,避免 sp 循环依赖.
- 控制块里的弱计数记录 wp 的数量,当 sp 和 wp 数量全部为0后,控制块才会被析构。
std::make_unique, std::make_shared
- make_unique 是 C++ 14的部分,可以自己写一个(暂略)
- 优先使用 make_shared,因为速度更快:控制块和托管对象处在同一内存区域。
- 不适用的情况包括:自定义删除器,直接传递大括号初始化。
- 使用 make_shared 后,如果 sp 数量已经为0,但还有 wp,那么内存不会被释放(对象已经被析构,控制块未被析构).