智能指针特点
智能指针封装了裸指针, 内部还是裸指针的调用
智能指针使用RAII特点, 将对象生命周期使用栈来管理
智能指针区分了所有权, 因此使用责任更为清晰
智能指针大量使用操作符重载和函数内联特点, 调用成本和裸指针无差别
unique_ptr
unique_ptr被设计称为一个零额外开销的智能指针, 使用它, 应该相比于手写new和delete没有额外开销, 不管是时间还是空间上.
独占拥有权, 不支持移动拷贝, 只支持移动(所有权转移)
shared_ptr
- 共享所有权
- 存储成本较裸指针多一个引用计数指针
- 接口慎用(会导致蔓延问题)
- 线程安全, 引用技术增减会减慢多核性能
- 支持拷贝构造, 支持移动
unique_ptr使用起来很困难的原因, 在于unique_ptr解决重复释放的方式是禁止拷贝, 这样虽然有效率搞的优势, 但倒是使用困难, 容易犯错等.
相比之下, 牺牲效率换来自由度的shared_ptr则允许拷贝, 它解决重复释放的方式是通过引用计数
- 当一个shared_ptr初始化时, 将计数器设为1
- 当一个shared_ptr被拷贝时, 计数器加1
- 当一个shared_ptr被解构时, 计数器减1. 减到0时, 则自动销毁它指向的对象.
- 从而可以保证, 只要还有存在哪怕一个指针指向该对象, 就不会被解构.
weak_ptr
有时候我们希望维护shared_ptr的弱引用weak_ptr, 即: 弱引用的拷贝与解构不影响其引用计数器
之后有需要时, 可以通过lock()随机产生一个新的shared_ptr作为强引用. 但不locak的时候不影响计数
如果失效(计数器归零)则expired()会返回false, 且lock()也会返回nullptr
缺点:
- shared_ptr需要维护一个atomic的引用计数器, 效率低, 需要额外的一块管理内存, 访问实际对象需要二级指针, 而且deleter使用了类型擦除技术.
- 全部使用shared_ptr, 可能出现循环引用之类的问题, 导致内存泄漏, 依然需要使用不影响计数的原始指针或者weak_ptr来避免 例如:
1 |
|
循环引用
多个指针彼此指向对方

解决方案1:
只需要把其中逻辑上”不具有所属权”的哪一个改成weak_ptr即可:
1 |
|
解决方案2(续):
甚至还可以更好! 刚才提到unique_ptr的应用场景是”当该对象仅仅属于我时”. 既然都用了原始指针(假定它释放前我必然被释放). 因此我们完全可以把m_child变成一个标志这”完全所有权”的unique_ptr
这样也不需要shared_ptr维护一个原子计数器的开销了.
1 |
|