开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
C++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露,二次释放,程序发生异常时内存泄露等问题,使用智能指针能更好的管理堆内存。
C++的智能指针是一个类,用于存储指向动态分配对象的指针,负责自动释放该对象,防止内存泄露以及多次释放同一块内存空间
C++的智能指针有四种:shared ptr、weak ptr、unique ptr、auto ptr,auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用,下面将对这四种智能指针做详细介绍
shared_ptr
采用引用计数,允许多个智能指针指向同一个对象,在内部会维护一份引用计数,用于记录资源被多少个对象共享,每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,每当减少一个智能指针指向对象时,引用计数会减1,当计数为0的时候会自动的释放该对象
引用计数是线程安全的,但是对象的读取需要加锁
- 因为shared_ptr有两个成员:指向资源的指针和指向引用的指针,shared_ptr可能在增加引用计数后改变指针方向
相关操作
初始化
shared_ptr直接构造和使用make_shared构造
- shared_ptr构造函数会执行两次内存申请,make_shared执行一次:shared_ptr在实现的时候需要有计数器和指针,当使用shared_ptr构造函数时会先申请数据的内存,再申请控制块;使用make_shared的时候是将数据和控制块一起申请
- 两次申请可能会导致内存泄露:new先申请内存后发生了异常,导致数据指针没有传递给shared_ptr没办法释放。使用make_ptr将数据和控制块一起申请就可以保证内存安全被释放
获取原始指针
获取原始指针是一种危险的行为:保存为裸指针可能会变成空悬指针,保存为shared_ptr则产生了独立指针
使用delete或保存为shared_ptr会导致对一块内存delete两次
指定删除器
使用shared_ptr管理非new对象或没有析构函数的类时需要为其传递合适的删除器
管理动态数组时需要指定删除器,shared_ptr的默认删除其不支持数组对象
注意问题
不要使用一个原始指针初始化多个shared_ptr
不要在函数实参中创建shared_ptr
- C++函数实参的计算顺序在不同编译器下约定不同,一般从右到左
function(shared_ptr<int>(new int),g()可能会先new int再调用g(),如果g()异常退出,但是shared_ptr还没有创建,会导致int内存泄露
通过shared_from_this返回this的shared_ptr
使用同一个指针this构造了两个智能指针,会导致this的内存重复释放
应该让目标类继承enable_shared_from_this类,使用基类的成员函数shared_from_this返回this的shared_ptr
循环引用
两个对象互相使用一个shared_ptr成员变量指向对方,会造成智能指针不能调用析构函数导致两个对象都无法释放
自己实现shared_ptr
template<typename T>
class shared_ptr{
private:
T *ptr;
long *use_count;
public:
shared_ptr(T* p);
shared_ptr(const shared_ptr<T> & orig);
~shared_ptr();
shared_ptr<T>& operator=(const shared_ptr<T> &orig);
T operator*();
T* operator->();
long getcount();
};
template<typename T>
shared_ptr<T>::shared_ptr(T *p){
ptr = p;
try{
use_count = new long(1);
}catch(...){
delete ptr;
ptr = nullptr;
p=nullptr;
use_count = nullptr;
}
}
template<typename T>
shared_ptr<T>::shared_ptr(const shared_ptr<T> &orig){
use_count = orig->use_count;
ptr = orig->ptr;
++(*use_count);
}
template<typename T>
shared_ptr<T>& shared_ptr<T>::operator=(const shared_ptr<T> &orig){
if(--(*use_count)==0){
delete ptr;
ptr = nullptr;
delete use_count;
use_count = nullptr;
}
++(*orig->use_count);
ptr = orig->ptr;
use_count = orig->use_count;
return *this;
}
template<typename T>
shared_ptr<T>::~shared_ptr(){
if(--(*use_count)==0){
delete ptr;
ptr = nullptr;
delete use_count;
use_count = nullptr;
}
}
template<typename T>
T shared_ptr<T>::operator*(){ return *ptr;}
template<typename T>
T* shared_ptr<T>::operator->(){ return ptr;}
template<typename T>
long shared_ptr<T>::getcount(){return *use_count;}
weak_ptr
弱引用:指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,只引用,不计数
weak_ptr没有重载操作符*和->,因为不共享指针,不能操作资源,主要是为了监视shared_ptr管理的资源是否存在以及解决循环引用问题
weak_ptr返回this指针指向shared_ptr
endable_shared_from_this类中有一个weak_ptr,用于观察this智能指针,调用shared_from_this会调用内部的weak_ptr的lock方法,将观察到的shared_ptr返回
解决循环引用问题
将两个对象的任意一个成员变量改为weak_ptr
unique_ptr
独享所有权的智能指针,不允许其他智能指针共享其内部的指针,不允许通过赋值将unique_ptr赋值给另一个unique_ptr
但是可以通过move将一个 unique_ptr对象转移给另一个unique_ptr对象,原unique_ptr就不再拥有原来指针的所有权的
make_unique是c++14的unique_ptr<int> p = make_unique<int>(10);
unique_ptr需要确定删除器类型
用同一个指针给多个unique_ptr初始化是危险操作,会导致资源重复释放
auto_ptr
C++11废弃了auto_ptr,其允许强制剥夺所有权, 会存在野指针风险,C++11中使用unique_ptr取代
当使用auto_ptr拷贝或赋值时,原对象会被置空:将对象赋给新的变量,释放原对象的内存,将原指针置为空