智能指针主要是为了解决内存忘记释放、内存过早释放、内存多次释放等问题,从而更好地对内存进行管理。(个人理解)
传统的裸指针虽然功能强大、使用灵活,但需要全程维护,容易出错。从而引入智能指针进行管理,可以把智能指针看做对于裸指针的包装。
智能指针有四种:auto_ptr(c++98)、shared_ptr(c++11)、unique_ptr(c++11)、weak_ptr(c++11),其中auto_ptr已被unique_ptr替代,不推荐使用auto_ptr。
智能指针是一种类模板。
一、shared_ptr共享式指针
shared_ptr允许多个指针指向同一个对象(内存)。其采用引用计数的机制,每多一个shared_ptr指向该对象,引用计数加一,每少一个shared_ptr,引用计数减一。当引用计数为0时,会自动析构该对象(释放该内存)。
shared_ptr的定义形式:
shared_ptr<int> 智能指针名;//智能指针是类模板,需要使用<>指定参数类型(指针指向的类型)
shared_ptr的两种初始化方式:
1.使用new初始化
一般形式:
shared_ptr<int> sptr(new int);
shared_ptr<int> sptr1(new int(10));
int *p = new int();//值初始化
shared_ptr<int> sptr2(p);//用裸指针也可以初始化,但是不推荐
2.使用make_shared()函数初始化
一般形式:
shared_ptr<int> sptr = make_shared<int>(100);//make_shared是函数模板,所以需要使用<>指定参数类型
shared_ptr<string> sptr = make_shared<string>(5, 'a');//调用string的构造函数进行string的初始化
make_shared是一种函数模板。推荐使用make_shared()进行shared_ptr的初始化,其缺点是不能自定义删除器。
shared_ptr拓展
1.use_count():返回当前shared_ptr所指内存的强引用数即shared_ptr的个数
shared_ptr<int> p1(new int(10));
shared_ptr<int> p2(p1);
weak_ptr<int> pw1(p1);
cout<<p1.use_count()<<endl;//此时返回值为2,p1所指内存有2个强引用,1个弱引用
2.reset():用于重置shared_ptr
shared_ptr<int> p1(new int(10));
p1.reset();//不带参数情况,将p1置空
p1.reset(new int(100));//带参数情况,一般输入为new出来的裸指针,此时p1指向新的内存
3.unique:判断此shared_ptr是否独占内存
若独占,返回true,否则返回false。
shared_ptr<int> p1 = make_shared<int> (10);
if(p1.unique()) {//此只有p1一个shared_ptr指向该内存,所以unique()返回true
cout<<"独占"<<endl;
}
else {
cout<<"不独占"<<endl;
}
4.shared_ptr同样可以使用*解引用,方式同普通指针
5.get():返回shared_ptr中保存的裸指针
shard_ptr<int> p1 = make_shared<int> (10);
int *p = p1.get();//get()返回p1中的裸指针
cout<<*p<<endl;
6.swap():交换两个shared_ptr指向
shared_ptr<string> ps1 = make_shared<string>("hello");
shared_ptr<string> ps2 = make_shared<string>("world");
cout << *ps1 << endl << *ps2 << endl;
swap(ps1, ps2);//交换两个指针指向的对象
ps1.swap(ps2);//交换两个指针指向
cout << *ps1 << endl << *ps2 << endl;
7.删除器
默认情况下,在内存的强引用计数减为0时,系统会调用delete()对内存进行释放。如果指定删除器,则系统会调用指定的删除器释放内存。
删除器形式一:函数
void mydeleter(int *p) {
//代码实现
cout<<"自定义删除器"<<endl;
delete p;//在自定义的删除器中一定要使用delete释放内存,否则会造成内存泄漏
}
int main() {
shared_ptr<int> p1(new int(10), mydeleter);//在参数列表中指定删除器
p1.reset();//p1置空,系统自动调用自定义删除器释放内存
return 0;
}
删除器形式二:lambda表达式
shared_ptr<int> ps1(new int[10], [](int *p){
delete[] p;
});
8.使用shared_ptr维护数组时的几种方法
第一种:从C++17开始,shared_ptr的模板参数支持传入<数据类型[]>用于对于数组内存的管理,同时提供了operator[]用于对数组元素直接进行访问,在释放数组时会调用delete[]对内存进行释放。
shared_ptr<int[]> p1(new int[]{1,2,3});//模板参数传入int[]
cout<<p1[0]>>p1[1]<<p1[2];//operator[]进行数组元素进行访问
p1.reset();//系统调用delete[]进行内存释放
第二种:在C++17前,shared_ptr不支持模板参数传入<数据类型[]>,同时不支持operator[],系统在释放内存时会默认调用delete进行释放。因此当我们使用shared_ptr管理数组时,系统会在释放内存时使用delete而报错。
情况如下:
shared_ptr<int> ps1(new int[3]{1,2,3));//使用shared_ptr管理数组
cout<<ps1.get()[0]<<endl;//C++17前不支持operator[]直接访问元素,需要使用get()[index]对元素进行访问
ps1.reset()//将ps1置空后,系统会调用delete对数组内存进行释放,从而导致错误
对于这种情况有两种解决办法:
方法1:C++提供了一种标准库的类模板default_delete,可以用于数组内存的释放。可以在初始化shared_ptr时指定该删除器。
shared_ptr<int> ps1(new int[10], default_delete<int[]>());
方法2:自己定义删除器
void mydeleter(int *p) {
delete[] p;//用delete[]释放数组内存
}
int main() {
shared_ptr<int> ps1(new int[10], mydeleter);//指定自己定义的删除器
return 0;
}
注意:虽然两个shared_ptr指定的删除器可能不同,但只要它们指向同一个内存,那么它们就属于同一个类型。
二、weak_ptr
weak_ptr是用来辅助shared_ptr工作的一种智能指针,它指向由shared_ptr管理的内存,但不影响内存的生存期。(weak_ptr只会增减弱引用的计数,不会增减强引用的计数)。当shared_ptr需要释放内存时,不管有没有weak_ptr指向该内存,内存都会释放。因为weak_ptr作为一种辅助性的智能指针,其不影响所指内存的生存期,所以不能保证所指内存的有效性,因此不能通过weak_ptr对内存进行管理。
weak_ptr的初始化:一般通过shared_ptr进行初始化
shared_ptr<int> ps1(new int);
weak_ptr<int> pw1(ps1);
weak_ptr常用操作:
1.use_count():返回weak_ptr所指内存的强引用计数,即shared_ptr的数量
shared_ptr<int> ps1(new int);
weak_ptr<int> pw1(ps1);
cout<<pw1.use_count()<<endl;//use_count()返回该weak_ptr指向内存的强引用计数,即有多少shared_ptr指向该内存,此情况应为1
2.expired():判断weak_ptr所指内存是否过期,若内存已被释放则返回true,若未被释放则返回false
shared_ptr<int> ps1(new int);
weak_ptr<int> pw1(ps1);
cout<<pw1.expired()<<endl;//此时内存未被释放,返回false
ps1.reset();//ps1置空,内存释放
cout<<pw1.expired()<<endl;//返回true
3.reset():用法同shared_ptr
4.lock():判断weak_ptr指向内存是否被释放,若未释放,则返回指向该内存的shared_ptr,若已释放,则返回空的shared_ptr。
shared_ptr<int> ps1 = make_shared<int>(10);
weak_ptr<int> pw1(ps1);
auto ps2 = pw1.lock();//此时pw1所指内存未释放,lock()返回一个指向该内存的shared_ptr,用一个shared_ptr来接收,强引用+1
水平有限,欢迎批评指正!