C++智能指针auto_ptr、unique_ptr、shared_ptr、weak_prt详解
创始人
2024-05-23 11:50:27
0

目录

一.为什么要使用智能指针

二.auto_ptr

三.unique_ptr

四.shared_ptr

五.weak_ptr


智能指针均定义在头文件中:

#include

同时每种智能指针都是以类模板的方式实现 

一.为什么要使用智能指针

  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 Name();
auto_ptr Name(nullptr);

②.创建一个auto_ptr指针同时明确其指向:

auto_ptr Name(new TypeName);
auto_ptr Name(new TypeName(value)); //初始化指针

eg:

auto_ptr au(new int);

由此创建一个auto_ptr指针指针,其指向是可容纳一个整数的堆储存空间。

auto_ptr不支持数组,并且不要将该auto_ptr智能指针往容器中放。

  创建完auto_ptr指针并将指针指向某个对象后,使用方法与普通的指针类似, 可以通过对指针解引用获得指针所指的对象本身,也可以使用类成员函数get(),该函数显示返回auto_ptr指针指向的对象。

C++代码示例:

#include
#include
#include
using namespace std;int main()
{auto_ptr 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
#include
#include
#include
using namespace std;int main()
{vector> v1(2);auto_ptr a1(new string("GodFishhh"));auto_ptr a2(new string("AFish"));v1[0] = a1;v1[1] = a2;//auto_ptr au(new string);//au = v1[1];auto_ptr au = v1[1]; //重载赋值运算符会分配内存,所以可以直接创建对象然后指向别处cout << (au.get()) << endl;cout << v1[1].get() << endl;system("pause");return 0;
}

程序运行结果:

  可知此时v1[1]的地址已被设置为NULL,其罪魁祸首就是语句auto_ptr au = v1[1],此行代码使用auto_ptr类的赋值运算符,将v1[1]中的指针所有权转移,并将v1[1]的指针设置为空指针NULL。 

②.auto_ptr支持复制构造,为了确保指针所有者唯一,对auto_ptr指针使用复制构造会转移指针的所有权。

函数传参示例:

类成员作为参数按值传递时通过复制构造函数生成副本

#include
#include
#include
#include
using namespace std;void test(auto_ptrau) //参数是类名为auto_ptr是类对象
{return;
}int main()
{auto_ptrau(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对象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 Name():
unique_ptr Name(nullptr);

②.创建一个unique_ptr同时明确其指向:

unique_ptr Name(new TypeName);
unique_ptr Name(new TypeName(value));

eg:

unique_ptr un(new int);

由此创建出了一个unique_ptr智能指针,其指向是可容纳一个整数的堆储存空间。 

4.由于unique_ptr类型指针不共享各自拥有的堆内存,因此C++11标准中的unique_ptr模板类没有提供拷贝构造函数,只提供了移动构造函数。

移动构造函数使用语法:

unique_ptr un1(new int);
unique_ptr un2(move(p4)); //使用移动构造函数
//unique_ptr un3(un1); //使用复制构造函数,而unique_ptr堆内存不共享,无法使用

使用移动构造函数后,un2会获得un1所指堆空间的所有权,同时un1将变为空指针NULL。

 代码测试:

#include
#include
#include
#include
using namespace std;int main()
{unique_ptr un1(new string);cout << "使用移动构造函数前的un1:" << un1 << endl;unique_ptr un2(move(un1));cout << "使用移动构造函数后的un1:" << un1 << endl;cout << "un2:" << un2 << endl;system("pause");return 0;
}

程序运行结果:

由结果可知此时作为移动构造函数参数的un1已经变为空指针NULL了。

5.默认情况下,unique_ptr指针采用default_delete方法释放内存,同时我们也可以根据实际场景自定义释放规则,但与shared_ptr指针不同,为unique_ptr自定义释放规则只能采用函数对象的方式。

//自定义的释放规则
struct myDelete
{void operator()(int *p) {delete p;}
};
unique_ptr p6(new int);
//unique_ptr p6(new int, myDel());

6.unique_ptr模板类提供的成员方法:

unique_ptr指针可调用的成员函数

 同时C++11标准还支持unique_ptr指针之间,以及unique_ptr和nullptr之间做 ==,!”,<,<=,>,>=运算。

7.unique_ptr指针的基本用法(release(),reset() )示例:

C++代码:

#include
#include
#include
#include
using namespace std;int main()
{unique_ptr 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 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 Name();
shared_ptr Name(nullptr);

②.创建一个shared_ptr指针并明确其指向:

shared_ptr Name(new TypeName);

eg:

shared_ptr sh(new string(5));

 如上语句,我们就成功构建了一个shared_ptr智能指针,其指向一个存有五个string类型数据的堆内存空间

3.C++11标准提供make_shared模板函数,用于初始化shared_ptr智能指针

shared_ptr sh = make_shared(5);

即构建了一个shared_ptr智能指针,其指向一个存有五个string类型数据的堆内存空间。

与如下创建方法相同:

shard_ptr sh(new string(5));

4.shared_ptr模板提供了相应的复制构造函数和移动构造函数

①.复制构造函数:

shared_ptr sh1(sh2);

其中sh1和sh2均为shared_ptr智能指针。(sh2作为左值)

如果sh2为空智能指针,则sh1也同样为空智能指针。

如果sh2不是空智能指针,则表明sh1和sh2指向同一块堆内存空间,因此该堆空间的的引用计数会加一。 

②.移动构造函数:

shared_ptr sh1(move(sh2));

move的参数sh2会被强制转换成对应的右值。

同时调用移动构造函数会使sh1获得sh2所指堆空间的所有权,而参数sh2则会变空智能指针。

eg:

C++代码示例:

#include
#include
#include
#include
using namespace std;int main()
{shared_ptr sh1(new string("GodFishhh"));cout << "移动构造之前的sh1:" << sh1.get() << endl;shared_ptr 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 sh1(new int[10], default_delete());//自定义释放规则
void deleteInt(int*p) 
{delete []p;
}
//初始化智能指针,并自定义释放规则
shared_ptr sh2(new int[10], deleteInt);

 shared_ptr默认的释放规则是不支持数组的,若是申请的动态数组则必须通过自定义的释放规则来释放申请的堆内存

6.shared_ptr模板类提供的成员方法:

shared_ptr模板类常用方法:

除此之外,C++11 标准还支持同一类型的 shared_ptr 对象,或者 shared_ptr 和 nullptr 之间,进行 ==,!=,<,<=,>,>= 运算。

7.shared_ptr智能指针的基本用法示例:

C++代码:

#include
#include
#include
#include
using namespace std;int main()
{//use.count()函数:返回当前shared_ptr对象指向相同的堆内存的shared_ptr对象数量//复制构造函数shared_ptr sh1(new string("GodFishhh"));cout << "复制构造前的sh1/*sh1;" << sh1 << "/" << *sh1 << endl;cout << "sh1所指堆空间含有多少对象指向:" << sh1.use_count() << endl;shared_ptr sh2(sh1);cout << "复制构造后的sh1/*sh1;" << sh1 << "/" << *sh1 << endl;cout << "通过复制构造初始化的sh2/*sh2" << sh2 << "/" << *sh2 << endl;cout << "sh1所指堆空间含有多少对象指向:" << sh1.use_count() << endl;cout << "-----------------------" << endl;//移动构造函数shared_ptr sh3(new string("AFish"));cout << "移动构造前的sh3:" << sh3 << endl;cout << "sh3所指堆空间含有多少对象指向:" << sh3.use_count() << endl;shared_ptr 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 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 we();
weak_ptr we(nullptr);

②.通过已有的weak_ptr指针创建一个新的weak_ptr指针

weak_ptr we2(we1);

若we1为空指针,则we2也为空指针。

若we1不是空指针,则we1指向某一个shared_ptr指针拥有的堆内存,同时we2也指向该堆内存空间,但只能访问没有所有权。

③.利用已有的shared_ptr为weak_ptr指针初始化(weak_ptr指针通常指向某一shared_ptr指针拥有的堆内存)

shared_ptr sh(new string);
weak_ptr we(sh);

此时we和sh指针指向相同的堆内存空间,但是堆内存空间的引用次数不会改变。

3.weak_ptr模板类提供的成员方法:

weak_ptr可调用的成员方法: 

 同时,weak_ptr 模板类没有重载 * 和 -> 运算符,因此 weak_ptr 类型指针只能访问某一 shared_ptr 指针指向的堆内存空间,无法对其进行修改。

4.weak_ptr智能指针基本用法示例:

C++代码:

#include
#include
#include
#include
using namespace std;int main()
{//use_count()查询与当前weak_ptr指针相同的shared_ptr指针的数量//通过shared_ptr指针初始化weak_ptr指针;shared_ptr sh1(new string("GodFishhh"));shared_ptr sh2(sh1);weak_ptr we1(sh1);weak_ptr we2(sh1);cout << "此时与we1指针相同的shared_ptr指针数量:" << we1.use_count() << endl;shared_ptr 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 sh4 = we1.lock();cout << "we1.lock()的返回值sh4:" << sh4 << endl;shared_ptr sh5 = we2.lock();cout << "we2.lock()的返回值sh5:" << sh5 << endl;cout << "sh1:" << sh1 << endl;system("pause");return 0;
}

程序运行结果:

相关内容

热门资讯

寒假见闻作文400字 寒假见闻作文400字合集五篇  在日常生活或是工作学习中,大家最不陌生的就是作文了吧,作文是经过人的...
爱笑的我作文650字 爱笑的我作文650字  我就是我,一个平凡的我;我就是我,一个无忧无虑的我;我就是我,一个魔术师一样...
想象作文400字 想象作文400字(精选118篇)  在学习、工作乃至生活中,大家都尝试过写作文吧,借助作文可以宣泄心...
《自己》张杰歌词 《自己》张杰歌词  《歌手》2017第十场节目中,头顶淘汰高压的张杰愈战愈勇,以《自己》这首歌曲再次...
荷花作文300字 关于荷花作文300字6篇  在生活、工作和学习中,大家对作文都再熟悉不过了吧,借助作文可以宣泄心中的...
中秋节的作文900字 关于中秋节的作文900字3篇  在日常学习、工作和生活中,大家总少不了接触作文吧,作文根据体裁的不同...
感恩生活的作文 感恩生活的作文  在日常学习、工作和生活中,大家最不陌生的就是作文了吧,借助作文可以宣泄心中的情感,...
介绍春节的英语作文 关于介绍春节的英语作文三篇  在日常的学习、工作、生活中,大家对作文都再熟悉不过了吧,作文是从内部言...
给远方的朋友写一封信作文70... 给远方的朋友写一封信作文700字  亲爱的慧纸:  你好呀!  因为作文里不给使用真实的姓名,我就用...
感念师恩作文 感念师恩作文如果觉得很不错,欢迎点评和分享~感谢你的阅读与支持!  感念师恩  假如我能搏击蓝天,那...
欢乐的节日作文 欢乐的节日作文  在平时的学习、工作或生活中,大家一定都接触过作文吧,作文可分为小学作文、中学作文、...
老师不在的时候作文450字 老师不在的时候作文450字  2012年9月27日 星期四 晴  今天第二节课,我们上语文课,老师让...
植树节的作文600字 【推荐】植树节的作文600字合集九篇  在生活、工作和学习中,大家都尝试过写作文吧,根据写作命题的特...
我快乐的家作文 我快乐的家作文15篇  在日常生活或是工作学习中,许多人都写过作文吧,借助作文可以宣泄心中的情感,调...
母亲节的作文300字 关于母亲节的作文300字锦集10篇  在日常生活或是工作学习中,大家都经常看到作文的身影吧,作文是人...
龙年的春晚作文 龙年的春晚作文2012已经来到了,新年的钟声已敲响,但在中国的阴历来说,我们要等到一月二十日除夕夜过...
生日的作文700字 关于生日的作文700字十篇  无论在学习、工作或是生活中,大家对作文都不陌生吧,借助作文可以宣泄心中...
秋语作文750字 秋语作文750字  秋,一个金色的季节,一个美好的季节。这个季节里有果实的飘香,有叶子的金黄,还有南...
那一刻我的世界春暖花开作文 那一刻我的世界春暖花开作文600字(精选34篇)  无论在学习、工作或是生活中,大家都写过作文,肯定...
感恩的作文600字 关于感恩的作文600字8篇  在日常学习、工作抑或是生活中,大家一定都接触过作文吧,借助作文可以宣泄...