- 智能指针实质是一个对象,行为表现的像指针
- RAII(资源获取即初始化)对普通指针进行封装
- 值语义转换成引用语义
- 避免出错跳转catch导致try中的delete没有运行
智能指针的使用
shared_ptr使用
shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
- 初始化:构造函数或make_shared
- 拷贝和赋值
- 拷贝使引用+1;
- 赋值使得原对象引用-1;
- get获得原始指针
- 注意不要用一个原始指针初始化多个shared_ptr,会造成double free
- 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。
循环引用
```cpp class B; // 前置声明 class A { public: shared_ptr ptr; };
class B { public: shared_ptr ptr; };
int main() { while(true) { shared_ptr pa(new A()); shared_ptr pb(new B()); pa -> ptr = pb; pb -> ptr = pa; } return 0; }
<a name="sDSbQ"></a>
## ![](https://cdn.nlark.com/yuque/0/2021/jpeg/2728415/1615903479013-4cecba9c-1ff3-402e-b326-7fd5ac958ce5.jpeg#align=left&display=inline&height=371&margin=%5Bobject%20Object%5D&originHeight=371&originWidth=562&size=0&status=done&style=none&width=562)
上图中,class A和class B的对象各自被两个智能指针管理,也就是A object和B object引用计数都为2,为什么是2?
分析class A对象的引用情况,该对象被main函数中的pa和class B对象中的ptr管理,因此A object引用计数是2,B object同理。
在这种情况下,在main函数中一个while循环结束的时候,pa和pb的析构函数被调用,但是class A对象和class B对象仍然被一个智能指针管理,A object和B object引用计数变成1,于是这两个对象的内存无法被释放,造成内存泄漏,如下图所示<br />![](https://cdn.nlark.com/yuque/0/2021/jpeg/2728415/1615903505786-b6bd3579-07b5-49dc-bbb6-c93e548b75fd.jpeg#align=left&display=inline&height=401&margin=%5Bobject%20Object%5D&originHeight=401&originWidth=557&size=0&status=done&style=none&width=557)
<a name="XeWoV"></a>
## unique_ptr使用
同一时刻只能有一个unique_ptr指向给定对象(通过禁止**拷贝语义,**只有**移动语义**来实现)。
- 生命周期:unique_ptr指针创建时开始,直到离开作用域
- 对象销毁(默认使用delete操作符,用户可指定其他操作)
- 智能指针所指对象改变:构造函数,reset方法重新指定,release,移动语义
<a name="fAbWk"></a>
## weak_ptr(资源观测权)的使用
为配合shared_ptr的使用,不具备普通指针的行为,没有重载`operator*和->`,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
- 可从shared_ptr或者weak_ptr对象构造,获得资源观测权
- 构造不会引起指针引用计数增加
- use_count():引用计数;expired(),不复存在
- lock():从被观测的shared_ptr中获取shared_ptr对象,但当expired()==true时,lock()函数将返回一个存储空指针的shared_ptr
<a name="S5NGe"></a>
## 实现原理
> 智能指针将一个**计数器与类指向的对象**相关联,引用计数跟踪该类有多少个对象共享同一指针
- 每次创建类的对象时,初始化指针并将引用计数置1;
- 拷贝构造函数:拷贝指针并增加对应的引用计数。
- 赋值拷贝:减少左操作数的引用计数并增加右操作数
- 析构函数: 减少引用计数
- 重载`->`和`*`
- 自动销毁:栈对象的有限作用域以及临时对象(**有限作用域**实现)析构函数释放内存
```cpp
template<typename T>
class SmartPointer{
private:
T* _ptr;
size_t* _count;
public:
SmartPointer(T* ptr==nullptr):
_ptr(ptr){
if(_ptr){
_count=new size_t(1);
}else{
_count=new size_t(0);
}
}
SmartPointer(const SmartPointer& _sp){
if(this!=&_sp){
this->_ptr=_sp._ptr;
*this->_count=_sp._count;
(*this->_count)++;
}
}
SmartPointer& operator=(const SmartPointer& _sp){
if(this!=&sp){
if(this->_ptr){
(*this->_count)--;
if((*this->_count)==0){
delete this->_count;
delete this->_ptr;
}
}
this->_ptr=_sp._ptr;
this->_count=_sp._count;
(*this->_count)++;
return *this;
}
}
T& operator*() const{
assert(this->_ptr!=nullptr);
return *(this->_ptr);
}
T* operator->() const {
assert(this->_ptr!=nullptr);
return this->_ptr;
}
T* get() const {
return this->_ptr;
}
~SmartPointer(){
(*this->_count)--;
if((*this->_count)==0){
delete this->_count;
delete this->_ptr;
}
}
size_t use_count() const{
return *(this->_count);
}
bool expired() const {
return use_count()==0;
}
}