关于智能指针的一些理解
智能指针
智能指针采用RAII思想,用来动态的分配内存,防止内存泄漏。
智能指针的分类:auto_ptr、unique_ptr、shared_ptr、weak_ptr
auto_ptr:当拷贝auto_ptr时,原指针会变为nullptr,引发悬空指针,已弃用
unique_ptr:独占所有权,同一时间只有一个unique_ptr管理一个对象,不支持拷贝,unique_ptr删除了拷贝构造函数和赋值重载函数,使用make_unique创建(c++14及以上),可以使用move()转移所有权,定制删除器,通过模板参数传入一个可调用对象,在编译时就能确定删除器的类型
//创建unique_ptr
auto ptr=std::make_unique<int>(42);
//所有权转移
//原理: move函数将ptr转换为右值,触发unique_ptr的移动构造/赋值函数,
//进行所有权转移,ptr指向nullptr
std::unique_ptr<int> ptr2=std::move(ptr);//ptr变为nullptr
//定制删除器
void close_file(FILE* fp)
{if(fp) std::fclose(fp);
}
//不能使用std::make_unique创建了
std::unique_ptr<FILE,void(*)(FILE*)> file_ptr(fopen("data.txt","r"),close_file);shared_ptr:共享所有权,多个shared_ptr指向同一个对象,使用引用计数来管理,当最后一个shared_ptr被销毁时,引用计数为0,对象被释放,引用计数是原子操作,是线程安全的,但是所管理的对象不是线程安全的。缺点,可能出现循环引用,当两个对象互相使用shared_ptr指向对方时,引用计数永远不会为0,对象不会释放,造成内存泄漏。解决办法,将其中一个shared_ptr改为weak_ptr,打破循环。定制删除器,使用构造函数传参,不改变shared_ptr的类型,删除器存储在控制块中,该控制块动态分配,会占用额外内存,在运行时确定删除器的类型
//创建shared_ptr,使用make_shared创建一次性分配足够多的内存,存放对象和控制块,效率更高
auto ptr1=std::make_shared<int>(100);//引用计数为1
//循环引用
class B;//前向声明
class A
{
public:std::shared_ptr<B> b_ptr;
};
class B
{
public:std::shared_ptr<A> a_ptr;
};
int main()
{auto a=std::make_shared<A>();//a的引用计数为1auto b=std::make_shared<B>();//b的引用计数为1a->b_ptr=b;//b的引用计数为2b->a_ptr=a;//a的引用计数为2std::cout<<"a的引用计数: "<<a.use_count()<<std::endl;std::cout<<"b的引用计数: "<<b.use_count()<<std::endl;return 0;
}//main函数结束,局部变量a和b析构,但是对象A和B的引用计数不为0,不会销毁
//解决办法: 使用weak_ptr打破循环
class B
{
public:std::weak_ptr<A> a_ptr;
};
//定制删除器,使用lambda传参
std::shared_ptr<FILE> shared_file(fopen("data.txt","r"),[](FILE* f){if(f) fclose(f);});
weak_ptr:是一种不增加引用计数的智能指针,用于解决shared_ptr的循环引用问题,weak_ptr是弱引用,不能直接访问对象,必须先使用lock()转换为shared_ptr,才能访问,通常使用shared_ptr创建,lock()用来检查对象存在并增加引用计数,expired()用来检查所指向的对象是否还在
//创建weak_ptr
std::shared_ptr ptr1=std::make_shared<int>(20);
std::weak_ptr ptr2=ptr1;
//访问weak_ptr指向的对象
std::shared_ptr ptr3=ptr2.lock();
//判断weak_ptr指向的对象是否存在
if(!ptr1.expired())//对象存在
{//但是此刻,对象可能过期auto ptr4=ptr1.lock();//提升为shared_ptr进行管理
}
//推荐使用lock()
if(auto ptr5=ptr1.lock())
{//...
}