目录
一.为什么要使用智能指针
二.auto_ptr
三.unique_ptr
五.weak_ptr
智能指针均定义在头文件<memory>中:
#include<memory>
同时每种智能指针都是以类模板的方式实现
一.为什么要使用智能指针
C++的内存管理中,每当使用new来申请新的内存空间时,则必须使用delete来完成对应的内存释放。但是有些时候无法避免程序在还未执行到delete语句时就跳转了或者没有执行到最后的delete语句就返回了,如果我们不在每一个可能跳转或者返回的窗口前释放资源,则会造成内存的泄露。
使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数则会自动释放资源。
二.auto_ptr
1.auto_ptr由C++98引入,但从C++11开始,引入了unique_ptr来替代auto_ptr。
2.创建一个auto_ptr智能指针:
①.创建空的auto_ptr指针:
auto_ptr<TypeName> Name();
auto_ptr<TypeName> Name(nullptr);
②.创建一个auto_ptr指针同时明确其指向:
auto_ptr<TypeName> Name(new TypeName);
auto_ptr<TypeName> Name(new TypeName(value)); //初始化指针
eg:
auto_ptr<int> au(new int);
由此创建一个auto_ptr指针指针,其指向是可容纳一个整数的堆储存空间。
auto_ptr不支持数组,并且不要将该auto_ptr智能指针往容器中放。
创建完auto_ptr指针并将指针指向某个对象后,使用方法与普通的指针类似, 可以通过对指针解引用获得指针所指的对象本身,也可以使用类成员函数get(),该函数显示返回auto_ptr指针指向的对象。
C++代码示例:
#include<iostream>
#include<memory>
#include<string>
using namespace std;
int main()
{
auto_ptr<string> au(new string("GodFishhh"));
cout << *au << endl;
cout << au.get() << endl;
cout << *(au.get()) << endl;
system("pause");
return 0;
}
程序运行结果:
可知au.get()返回智能指针auto_ptr所指向对象的地址,而*(au.get())与*au的效果相同,得到的是指针指向的对象本身。
3.auto_ptr的弊端
①.auto_ptr支持operator=,为了确保指针所有者唯一,对auto_ptr指针使用赋值运算符会转移指针的所有权。
数组保存auto_ptr示例:
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
int main()
{
vector<auto_ptr<string>> v1(2);
auto_ptr<string> a1(new string("GodFishhh"));
auto_ptr<string> a2(new string("AFish"));
v1[0] = a1;
v1[1] = a2;
//auto_ptr<string> au(new string);
//au = v1[1];
auto_ptr<string> au = v1[1]; //重载赋值运算符会分配内存,所以可以直接创建对象然后指向别处
cout << (au.get()) << endl;
cout << v1[1].get() << endl;
system("pause");
return 0;
}
程序运行结果:
可知此时v1[1]的地址已被设置为NULL,其罪魁祸首就是语句auto_ptr<string> au = v1[1],此行代码使用auto_ptr类的赋值运算符,将v1[1]中的指针所有权转移,并将v1[1]的指针设置为空指针NULL。
②.auto_ptr支持复制构造,为了确保指针所有者唯一,对auto_ptr指针使用复制构造会转移指针的所有权。
函数传参示例:
类成员作为参数按值传递时通过复制构造函数生成副本
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
void test(auto_ptr<string>au) //参数是类名为auto_ptr<string>是类对象
{
return;
}
int main()
{
auto_ptr<string>au(new string("GodFishhh"));
cout << "将auto_ptr对象au作为参数传入函数前:" << endl;
cout << *(au.get())<< endl;
cout << "将auto_ptr对象au作为参数传入函数后:" << endl;
test(au);
cout << *(au.get()) << endl;
system("pause");
return 0;
}
程序运行结果:
程序出现了崩溃
程序崩溃的原因在于此行代码test(au),执行test函数时传入实参auto_ptr<string>对象au,此举都会导致原变量au的指针所有权转移,导致此时实参au的指针已被设置为空NULL。
基于上述原因,auto_ptr在C++11中被抛弃,引入的更加完善的智能指针unique_ptr。
三.unique_ptr
1.unique_ptr由C++11引入,旨在替代不安全的auto_ptr。
2.unique_ptr指针指向的堆内存无法与其他unique_ptr指针共享,因此每个unique_ptr指针都独自拥有对其堆内存空间的所有权。
3.创建一个unique_ptr智能指针:
①.创建空的unique_ptr指针:
unique_ptr<TypeName> Name():
unique_ptr<TypeName> Name(nullptr);
②.创建一个unique_ptr同时明确其指向:
unique_ptr<TypeName> Name(new TypeName);
unique_ptr<TypeName> Name(new TypeName(value));
eg:
unique_ptr<int> un(new int);
由此创建出了一个unique_ptr智能指针,其指向是可容纳一个整数的堆储存空间。
4.由于unique_ptr类型指针不共享各自拥有的堆内存,因此C++11标准中的unique_ptr模板类没有提供拷贝构造函数,只提供了移动构造函数。
移动构造函数使用语法:
unique_ptr<int> un1(new int);
unique_ptr<int> un2(move(p4)); //使用移动构造函数
//unique_ptr<int> un3(un1); //使用复制构造函数,而unique_ptr堆内存不共享,无法使用
使用移动构造函数后,un2会获得un1所指堆空间的所有权,同时un1将变为空指针NULL。
代码测试:
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
int main()
{
unique_ptr<string> un1(new string);
cout << "使用移动构造函数前的un1:" << un1 << endl;
unique_ptr<string> un2(move(un1));
cout << "使用移动构造函数后的un1:" << un1 << endl;
cout << "un2:" << un2 << endl;
system("pause");
return 0;
}
程序运行结果:
由结果可知此时作为移动构造函数参数的un1已经变为空指针NULL了。
5.默认情况下,unique_ptr指针采用default_delete<T>方法释放内存,同时我们也可以根据实际场景自定义释放规则,但与shared_ptr指针不同,为unique_ptr自定义释放规则只能采用函数对象的方式。
//自定义的释放规则
struct myDelete
{
void operator()(int *p)
{
delete p;
}
};
unique_ptr<int, myDelete> p6(new int);
//unique_ptr<int, myDelte> p6(new int, myDel());
6.unique_ptr<T>模板类提供的成员方法:
unique_ptr指针可调用的成员函数
同时C++11标准还支持unique_ptr指针之间,以及unique_ptr和nullptr之间做 ==,!”,<,<=,>,>=运算。
7.unique_ptr指针的基本用法(release(),reset() )示例:
C++代码:
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
int main()
{
unique_ptr<string> un1(new string("GodFishhh"));
cout << "release前的un1:" << un1.get() << endl;
//使用release函数转移智能指针unique_ptr所指堆空间的所有权
string* s1 = un1.release();
cout << "release后的un1:" << un1 << endl;
cout << "s1:" << s1 << "||" << "*s1:" << *s1 << endl;
unique_ptr<string> un2(new string("AFish"));
//使用reset函数使得un2获得某个堆内存的所有权:
cout << "reset前的s1/*s1:" << s1 <<"/"<<*s1<< endl;
cout << "reset前的un2/*un2:" << un2 <<"/"<<*un2<< endl;
un2.reset(s1);
cout << "reset后的s1:" << s1 << endl;
cout << "reset后的un2/*un2:" << un2 << "/" << *un2 << endl;
system("pause");
return 0;
}
程序运行结果:
由程序运行结果可知release()函数会转移unique_ptr指针un1所指向的堆空间所有权,并将un1设置为空指针NULL。reset(p)函数会将调用该函数的unique_ptr对象所指向的堆空间释放,并获得参数指针所指堆空间内存的所有权(其中参数p是一个普通指针)。
四.shared_ptr
1.与unique_ptr和weak_ptr不同之处在于多个shared_ptr可以共同使用同一块堆内存。同时shared_ptr指针在实现上采用的是引用计数机制,即使有一个shared_ptr指针放弃了堆内存的使用权,导致引用计数减一,也不会影响其他指向同一堆内存的shared_ptr指针。(只有当引用计数为零时,即没有shared_ptr指针指向某块堆内存时,堆内存才会自动释放)
2.创建一个shared_ptr智能指针:
①.创建空的shared_ptr指针:
shared_ptr<TypeName> Name();
shared_ptr<TypeName> Name(nullptr);
②.创建一个shared_ptr指针并明确其指向:
shared_ptr<TypeName> Name(new TypeName);
eg:
shared_ptr<string> sh(new string(5));
如上语句,我们就成功构建了一个shared_ptr智能指针,其指向一个存有五个string类型数据的堆内存空间
3.C++11标准提供make_shared<T>模板函数,用于初始化shared_ptr智能指针
shared_ptr<string> sh = make_shared<string>(5);
即构建了一个shared_ptr智能指针,其指向一个存有五个string类型数据的堆内存空间。
与如下创建方法相同:
shard_ptr<string> sh(new string(5));
4.shared_ptr<T>模板提供了相应的复制构造函数和移动构造函数
①.复制构造函数:
shared_ptr<int> sh1(sh2);
其中sh1和sh2均为shared_ptr智能指针。(sh2作为左值)
如果sh2为空智能指针,则sh1也同样为空智能指针。
如果sh2不是空智能指针,则表明sh1和sh2指向同一块堆内存空间,因此该堆空间的的引用计数会加一。
②.移动构造函数:
shared_ptr<int> sh1(move(sh2));
move的参数sh2会被强制转换成对应的右值。
同时调用移动构造函数会使sh1获得sh2所指堆空间的所有权,而参数sh2则会变空智能指针。
eg:
C++代码示例:
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
int main()
{
shared_ptr<string> sh1(new string("GodFishhh"));
cout << "移动构造之前的sh1:" << sh1.get() << endl;
shared_ptr<string> sh2(move(sh1));
cout << "移动构造之后的sh1:" << sh1.get() << endl;
cout << "通过移动构造初始化的智能指针sh2:" << sh2 << endl;
system("pause");
return 0;
}
程序运行结果:
可知作为移动构造函数的智能指针参数sh1变为了空指针。
5.在初始化shared_ptr智能指针时,还可以自定义所指堆内存的释放规则,当堆内存的引用计数为0时,则会优先调用我们自定义的释放规则。
自定义堆内存释放规则:
//指定 default_delete 作为释放规则
//默认的shared_ptr指针内存释放方式
shared_ptr<int> sh1(new int[10], default_delete<int[]>());
//自定义释放规则
void deleteInt(int*p)
{
delete []p;
}
//初始化智能指针,并自定义释放规则
shared_ptr<int> sh2(new int[10], deleteInt);
shared_ptr默认的释放规则是不支持数组的,若是申请的动态数组则必须通过自定义的释放规则来释放申请的堆内存
6.shared_ptr<T>模板类提供的成员方法:
shared_ptr<T>模板类常用方法:
除此之外,C++11 标准还支持同一类型的 shared_ptr 对象,或者 shared_ptr 和 nullptr 之间,进行 ==,!=,<,<=,>,>= 运算。
7.shared_ptr智能指针的基本用法示例:
C++代码:
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
int main()
{
//use.count()函数:返回当前shared_ptr对象指向相同的堆内存的shared_ptr对象数量
//复制构造函数
shared_ptr<string> sh1(new string("GodFishhh"));
cout << "复制构造前的sh1/*sh1;" << sh1 << "/" << *sh1 << endl;
cout << "sh1所指堆空间含有多少对象指向:" << sh1.use_count() << endl;
shared_ptr<string> sh2(sh1);
cout << "复制构造后的sh1/*sh1;" << sh1 << "/" << *sh1 << endl;
cout << "通过复制构造初始化的sh2/*sh2" << sh2 << "/" << *sh2 << endl;
cout << "sh1所指堆空间含有多少对象指向:" << sh1.use_count() << endl;
cout << "-----------------------" << endl;
//移动构造函数
shared_ptr<string> sh3(new string("AFish"));
cout << "移动构造前的sh3:" << sh3 << endl;
cout << "sh3所指堆空间含有多少对象指向:" << sh3.use_count() << endl;
shared_ptr<string> sh4(move(sh3));
cout << "移动构造后的sh3:" << sh3 << endl;
cout << "通过移动构造初始化的sh4:" << sh4 << endl;
cout << "sh4所指堆空间含有多少对象指向:" << sh4.use_count() << endl;
cout << "-----------------------" << endl;
//reset()函数:当函数没有实参时,该函数会使当前shared_ptr所指堆内存的引用计数减一,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的shared_ptr对象会获得该存储空间的所有权,并且引用计数的初始值为1
//没有实参时:
sh1.reset();
cout << "调用没有参数的reset函数后的sh1:" << sh1 << endl;
cout << "sh2所指堆空间含有多少对象指向:" << sh2.use_count() << endl;
cout << "-----------------------" << endl;
//有实参时:
//shared_ptr<string> sh5(new string("Fish"));
cout << "调用有参数的reset函数前的sh3所指空间有多少对象指向:" << sh3.use_count() << endl;
cout << "调用有参数的reset函数前的sh3:" << sh3 << endl;
sh3.reset(new string("Fish"));
cout << "调用有参数的reset函数后的sh3所指空间有多少对象指向:" << sh3.use_count() << endl;
cout << "调用有参数的reset函数前的sh3/*sh3:" << sh3 <<"/"<<*sh3<< endl;
cout << "-----------------------" << endl;
//swap():交换两个相同类型的shared_ptr智能指针的内容
cout << "swap前的*sh2和*sh4:" << *sh2 << " " << *sh4 << endl;
sh2.swap(sh4);
cout << "swap后的*sh1和*sh4:" << *sh2 << " " << *sh4 << endl;
cout << "-----------------------" << endl;
//unique():判断当前shared_ptr对象所指的堆内存,是否不再有其他shared_ptr对象再指向它
if (sh4.unique())
{
cout << "目前只有一个shared_ptr智能指针指向该堆空间" << endl;
}
else
{
cout << "目前有多个shared_ptr智能指针指向该堆空间" << endl;
}
cout << sh4.use_count() << endl;
system("pause");
return 0;
}
程序运行结果:
五.weak_ptr
1.weak_ptr智能指针通常不单独使用,只能和shared_ptr类型指针配合使用。weak_ptr类似于shard_ptr指针的一种辅助工具,借助weak_ptr指针,我们可以获得 shared_ptr指针的一些状态信息。
2.当weak_ptr指针的指向和某一个shared_ptr指针相同时,并不会使所指堆内存的引用计数加一;weak_ptr指针被释放时,也不会使所指堆内存的引用计数减一。因此,weak_ptr指针不会影响所指堆空间的引用计数。
3.创建一个weak_ptr智能指针
①.创建一个空的weak_ptr指针
weak_ptr<string> we();
weak_ptr<string> we(nullptr);
②.通过已有的weak_ptr指针创建一个新的weak_ptr指针
weak_ptr<string> we2(we1);
若we1为空指针,则we2也为空指针。
若we1不是空指针,则we1指向某一个shared_ptr指针拥有的堆内存,同时we2也指向该堆内存空间,但只能访问没有所有权。
③.利用已有的shared_ptr为weak_ptr指针初始化(weak_ptr指针通常指向某一shared_ptr指针拥有的堆内存)
shared_ptr<string> sh(new string);
weak_ptr<string> we(sh);
此时we和sh指针指向相同的堆内存空间,但是堆内存空间的引用次数不会改变。
3.weak_ptr<T>模板类提供的成员方法:
weak_ptr可调用的成员方法:
同时,weak_ptr<T> 模板类没有重载 * 和 -> 运算符,因此 weak_ptr 类型指针只能访问某一 shared_ptr 指针指向的堆内存空间,无法对其进行修改。
4.weak_ptr智能指针基本用法示例:
C++代码:
#include<iostream>
#include<memory>
#include<string>
#include<vector>
using namespace std;
int main()
{
//use_count()查询与当前weak_ptr指针相同的shared_ptr指针的数量
//通过shared_ptr指针初始化weak_ptr指针;
shared_ptr<string> sh1(new string("GodFishhh"));
shared_ptr<string> sh2(sh1);
weak_ptr<string> we1(sh1);
weak_ptr<string> we2(sh1);
cout << "此时与we1指针相同的shared_ptr指针数量:" << we1.use_count() << endl;
shared_ptr<string> sh3(sh1);
cout << "此时与we1指针相同的shared_ptr指针数量:" << we1.use_count() << endl;
//reset()将当前weak_ptr指针设为空指针
we1.reset();
//expire()判断当前weak_ptr指针是否过期(指针为空或者指针的堆内存已经释放)
if (we1.expired())
{
cout << "weak_ptr指针we1已经过期" << endl;
}
else
{
cout << "weak_ptr指针we1没有过期" << endl;
}
if (we2.expired())
{
cout << "weak_ptr指针we1已经过期" << endl;
}
else
{
cout << "weak_ptr指针we1没有过期" << endl;
}
//lock()如果当前weak_ptr已经过期,则返回一个空的shared_ptr指针;反之,该函数返回一个和当前weak_ptr指向相同的shared_ptr指针
shared_ptr<string> sh4 = we1.lock();
cout << "we1.lock()的返回值sh4:" << sh4 << endl;
shared_ptr<string> sh5 = we2.lock();
cout << "we2.lock()的返回值sh5:" << sh5 << endl;
cout << "sh1:" << sh1 << endl;
system("pause");
return 0;
}
程序运行结果: