前言
C++中没有像Java一样提供自动回收机制,程序员想要操作堆内存只能使用指针通过new或者malloc开辟,delete或者free释放。在日常工作中经常出现程序员没有准确的释放内存,有可能导致内存泄露,或者调用了一块已经被释放的内存而出现种种问题。智能指针的出现有效的解决了这类问题。智能指针是模板类而不是指针。智能指针实现基本原理原理是-资源分配即初始化RAII(Resource Acquisition Is Initialization)。具体做法就是定义一个类来封装资源的分配和释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的释放,可以保证资源正确的初始化和释放。下面就来介绍下几种常见的智能指针。
auto_ptr
auto_ptr是C++98加入标准库的智能指针,auto_ptr可以使用->和*(重载了这两个操作符)直接操作auto_ptr定义的智能指针;auto_ptr也有很多弊端,auto_ptr不能共享所有权;不能指向数组(因为析构函数调用的是delete而非delete[]);不能作为容器对象,因为STL容器中元素需要经常支持拷贝赋值等操作,而auto_ptr不能共享所有权所以必然不能在STL中使用,C++11以后版本已经弃用了auto_ptr。下面用例子说明下auto_ptr:
int *a=new int(10);
std::auto_ptr<int> p1(a);
上面代码如果想对指针a进行操作,会崩溃,就是因为auto_ptr不能共享所有权的特性导致的,在auto_ptr的拷贝构造函数中会将原来的指针置NULL,所以auto_ptr的最大缺点就是有内存崩溃的潜在风险,当你无意中使用到了指针a就会崩溃。
int *a=new int(10);
std::auto_ptr<int> p1(a);
std::auto_ptr<int> p2(a);
p2=p1;
上面代码如果使用p1指针依然会崩溃,因为auto_ptr的从写的赋值运算符函数也将入参置NULL,为了维护auto_ptr不能共享所有权的特性。所以个人推荐在程序中尽量不要使用auto_ptr智能指针,避免产生崩溃。
头文件:
#include
主要成员函数:
get():返回智能指针指向的内存地址
int *a=new int(10);
std::auto_ptr<int> p1(a);
qDebug()<<*(p1.get());//qt打印,打印出来的结果是10
reset():重新设置智能指针指向的对象
int *a=new int(10);
std::auto_ptr<int> p1(a);
p1.reset(new int(20));
qDebug()<<*(p1.get());//打印出来的结果是20
release():返回智能指针的内存地址并释放对象的所有权
int *a=new int(10);
std::auto_ptr<int> p1(a);
std::auto_ptr<int> p2(p1.release());
qDebug()<<*p2;
unique_ptr
unique_ptr是C++11提过的用于替换auto_ptr的智能指针,unique_ptr可以使用->和*直接操作智能指针,并且具有auto_ptr不能共享所有权的特性。但是unique_ptr提供的方案是不允许智能指针作为右值赋值给别人,如果你一定要将一个unique_ptr赋值给其他变量编译器会报错(因为unique_ptr删除了赋值运算符函数),而不会像auto_ptr一样编译器默许但是使用的时候直接崩溃。在C++14版本后可以使用make_unique初始化unique_ptr。
int *a=new int(10);
std::unique_ptr<int> p1(a);//通过get方法获取到的地址和a相同
//std::unique_ptr<int> p1=std::make_unique<int>(*a);//通过get方法获取到的地址和a不同
std::unique_ptr<int> p2;
p2=p1;
同样的例子使用unique_ptr声明的智能指针在p2=p1;进行赋值的时候编译器会报错。不允许这种赋值通过编译。
int *a=new int(10);
std::unique_ptr<int> p1(a);
std::unique_ptr<int> p2(a);
上面代码中如果在后面使用了指针p1或者p2程序依然会崩溃,因为两个unique_ptr指向同一块内存,在释放的时候会出现重复释放的问题。
头文件:
#include
主要成员函数:
get():返回智能指针指向的内存地址
int *a=new int(10);
std::unique_ptr<int> p1(a);
qDebug()<<*(p1.get());//qt打印,打印出来的结果是10
reset():重新设置智能指针指向的对象
int *a=new int(10);
std::unique_ptr<int> p1(a);
p1.reset(new int(20));
qDebug()<<*(p1.get());//打印出来的结果是20
release():返回智能指针的内存地址并释放对象的所有权
int *a=new int(10);
std::unique_ptr<int> p1(a);
std::unique_ptr<int> p2(p1.release());
qDebug()<<*p2;
shared_ptr
shared_ptr不同于unique_ptr和auto_ptr,shared_ptr允许多个指针指向同一个对象,shared_ptr通过计数来实现对内存的管理,在构造函数中每次新指针指向这块内存,计数器就会加1析构函数中每次有一个指针不需要指向这块内存计数减1,。如果计数变为0,那么就表示没有指针与这块内存关联就可以释放这块内存了。
头文件:
#include
主要成员函数:
swap:交换内存
int *n1=new int(10);
int *n2=new int(20);
std::shared_ptr<int> p1(n1);
std::shared_ptr<int> p2(n2);
qDebug()<<*p1<<*p2;//打印出来是10 20
p1.swap(p2);
qDebug()<<*p1<<*p2;//打印出来是20 10
use_count():获取引用计数值,共享指针为空时返回0
int *n1=new int(10);
std::shared_ptr<int> p1(n1);
qDebug()<<p1.use_count();//输出是1
std::shared_ptr<int> p2(p1);
qDebug()<<p1.use_count();//输出是2
reset():引用计数器减1,并可以从新赋值
int *n1=new int(10);
int *n2=new int(20);
std::shared_ptr<int> p1(n1);
qDebug()<<p1.use_count();//输出是1
std::shared_ptr<int> p2(p1);
qDebug()<<p1.use_count();//输出是2
p2.reset(n2);
qDebug()<<p1.use_count();//输出是1,因为p2重新指向了n2
get():获取原始指针
int *n1=new int(10);
std::shared_ptr<int> p1(n1);
qDebug()<<n1;//打印出地址
qDebug()<<p1.get();//和上面打印出的地址相同
unique():检测是否唯一
int *n1=new int(10);
std::shared_ptr<int> p1(n1);
qDebug()<<p1.unique();//输出为true
std::shared_ptr<int> p2(p1);
qDebug()<<p1.unique();//输出为false
还需要介绍1个使用shared_ptr常用到的函数但此函数并不是它的成员函数:
make_shared:构造共享指针
std::shared_ptr<int> p1=std::make_shared<int>(10);
qDebug()<<*p1;//输出为10
使用make_shared的好处主要是减少内存分配的次数可以一次分配内存,如果使用指针先new然后在用shared_ptr构造智能指针需要分配两次内存,new时候分配一次构造智能指针时候又分配了一次,如果构造函数不是public的话无法使用make_shared。
weak_ptr
虽然weak_ptr被定义为智能指针,但是该类型的指针通常不单独使用,只能和shared_ptr类型指针搭配使用。所以可以将weak_ptr视为shared_ptr的辅助工具。可以通过weak_ptr获取shared_ptr指针的一些状态。当weak_ptr和shared_ptr指向相同时,weak_ptr创建和释放并不会影响shared_ptr计数器的变化。通常wead_ptr的指针会指向share_ptr指针拥有的内存空间。
头文件:
#include
主要成员函数:
swap():交换内存
std::weak_ptr<int> pw(new int(10));
std::weak_ptr<int> pw2(new int(20));
pw.swap(pw2);
reset():将当前weak_ptr指针置空
std::weak_ptr<int> pw(new int(10));
pw.reset();
use_count():查看weak_ptr指向的shared_ptr指针的数量
std::shared_ptr<int> ps=std::make_shared<int>(10);
std::weak_ptr<int> pw(ps);
qDebug()<<pw.use_count();//输出为1
std::shared_ptr<int> ps2(ps);
qDebug()<<pw.use_count();//输出为2
expired():判断当前weak_ptr指针是否过期(指针为空,或者指针指向的内存被释放)
std::shared_ptr<int> ps=std::make_shared<int>(10);
std::weak_ptr<int> pw(ps);
qDebug()<<pw.expired();//输出为false,表示没过期
ps.~__shared_ptr();
qDebug()<<pw.expired();//输出为true,表示过期
lock():返回指向的shared_ptr指针,如果指针过期返回的shared_ptr指针为空
std::shared_ptr<int> ps=std::make_shared<int>(10);
std::weak_ptr<int> pw(ps);
if(NULL == pw.lock()){
qDebug()<<"null 1";
}
ps.~__shared_ptr();
if(NULL == pw.lock()){
qDebug()<<"null 2";//输出为null 2,证明此时weak_ptr指向的shared_ptr指针已过期
}
以上内容均是作者查阅资料加自己理解,如有疑问,望读者不吝赐教,谢谢!