1、2023-11-121 1 学习要点学习要点shared_ptrunique_ptrweak_ptr第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-122 2 学习目标学习目标第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针了解三种智能指针shared_ptr、unique_ptr和weak_ptr2023-11-123 3shared_ptr:是一个模板类,定义在是一个模板类,定义在头文件里。头文件里。shared_ptr对象会在其作用域结束时,自动销毁,对象会在其作用域结束时,自动销毁,如果该如果该shared_ptr是指向某动态对象是指
2、向某动态对象a的最后一个的最后一个shared_ptr,那么,那么a所在的内存会被释放。所在的内存会被释放。第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-124 4unique_ptr:也是一个模板类,同样定义在也是一个模板类,同样定义在头文件头文件里。与里。与shared_ptr不同的是,不同的是,unique_ptr是自己是自己“拥有拥有”一个指向的对象,也就是说不允许有两个一个指向的对象,也就是说不允许有两个或者以上的或者以上的unique_ptr指向同一个对象。在一个指向同一个对象。在一个unique_ptr对象的作用域结束时,对象的作用域结束时,u
3、nique_ptr指向指向的对象的内存被释放。为了保证的对象的内存被释放。为了保证unique_ptr对对象对对象的独有性,赋值、复制操作是不允许的。但有一个的独有性,赋值、复制操作是不允许的。但有一个例外,可以在函数中例外,可以在函数中return一个一个unique_ptr。第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-125 5weak_ptr:同样也是一个模板类,定义在同样也是一个模板类,定义在 头文件头文件中。它是为了辅助中。它是为了辅助shared_ptr而引入的一种智能指而引入的一种智能指针,它是一种弱引用,指向针,它是一种弱引用,指向shar
4、ed_ptr所管理的对所管理的对象,但不增加象,但不增加shared_ptr的引用计数。它存在的意的引用计数。它存在的意义就是协助义就是协助shared_ptr更好的完成工作,可以把它更好的完成工作,可以把它比做成一个秘书或助理。比做成一个秘书或助理。第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-126 6第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针shared_ptr sp空智能指针,可以指向类型为空智能指针,可以指向类型为T的对象的对象unique_ptr upp将将p作为一个判断条件,若作为一个判断条件,若p指向一个指向一个对象,
5、则为对象,则为true*p解引用解引用p,获得它指向的对象,获得它指向的对象p-mem等价于等价于(*p).memp.get()返回返回p中保存的指针中保存的指针swap(p,q)交换交换p和和q中的指针中的指针p.swap(q)表表3-2 shared_ptr3-2 shared_ptr和和unique_ptrunique_ptr都支持的操作都支持的操作2023-11-127 7第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针make_shared(args)返回一个返回一个shared_ptr,指向一个动态分配,指向一个动态分配的类型为的类型为T的对象。使用的对象。使用a
6、rgs初始化此对初始化此对象象shared_ptrp(q)p是是shared_ptr q的拷贝,此操作会递增的拷贝,此操作会递增q中的计数器,中的计数器,q中的指针必须能转换为中的指针必须能转换为T*p=qp和和q都是都是shared_ptr,所保存的指针必须,所保存的指针必须能相互转换,此操作会递减能相互转换,此操作会递减p的引用计数,的引用计数,递增递增q的引用计数,若的引用计数,若p的引用计数变为的引用计数变为0,则将其管理的原内存释放。则将其管理的原内存释放。p.unique()若若p.use_count()为为1,返回,返回true,否则返,否则返回回falsep.use_count
7、()返回与返回与p共享对象的智能指针数量,可能共享对象的智能指针数量,可能很慢,主要用于调试很慢,主要用于调试表表3-3 shared_ptr3-3 shared_ptr独有的操作独有的操作2023-11-128 81、shared_ptr:/指向指向string类型的空指针,默认初始化的智能指针是一个类型的空指针,默认初始化的智能指针是一个空指针空指针shared_ptr p1;shared_ptr p2;/指向指向int类型的空指针类型的空指针/指向一个值为指向一个值为10的的int类型的指针类型的指针shared_ptr p3(new int(10);第第3章章 类和对象类和对象-动态内
8、存与智能指针动态内存与智能指针2023-11-129 91、shared_ptr:/错误:不能将一个内置指针直接赋值给一个智能指针错误:不能将一个内置指针直接赋值给一个智能指针shared_ptr p4=new int(1);/p5为指向一个值为为指向一个值为1的的int类型的类型的shared_ptrshared_ptr p5=make_shared(1);第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1210101、shared_ptr:/p6为指向一个值为为指向一个值为9999999999的的string类型的类型的shared_ptrshared_p
9、tr p6=make_shared(10,9);/p7指向一个值初始化的指向一个值初始化的int,即,值为,即,值为0shared_ptr p7=make_shared();第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1211111、shared_ptr:shared_ptr的复制和赋值:当进行复制或赋值操作的复制和赋值:当进行复制或赋值操作时,时,每个每个shared_ptr都会记录有多少个其他都会记录有多少个其他shared_ptr指向相同的对象。指向相同的对象。/p指向的对象只有指向的对象只有p一个引用者一个引用者auto p=make_shared
10、(1);/p和和q指向相同对象,此对象有两个引用者指向相同对象,此对象有两个引用者auto q(p);第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-121212第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针1、shared_ptrv每个每个shared_ptr都有一个关联的计数器,通常称其都有一个关联的计数器,通常称其为引用计数为引用计数。v无论何时无论何时复制复制一个一个shared_ptr,计数器都会递增。,计数器都会递增。v当给当给shared_ptr赋予一个新值,或者是赋予一个新值,或者是shared_ptr被销毁(比如一个被销毁
11、(比如一个shared_ptr离开其作用域)时,计离开其作用域)时,计数器就会递减。数器就会递减。v一旦一个一旦一个shared_ptr的计数器变为的计数器变为0,它就会自动释,它就会自动释放所管理的对象放所管理的对象:2023-11-121313第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针使用使用shared_ptr注意事项:注意事项:(1)不要混合使用普通指针和)不要混合使用普通指针和shared_ptr指针指针(2)不要使用)不要使用get初始化另一个初始化另一个shared_ptr指针或为指针或为shared_ptr指针赋值指针赋值:2023-11-121414第
12、第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针表表3-4 3-4 定义和改变定义和改变shared_ptrshared_ptr的其他方法的其他方法shared_ptr p(q)p管理内置指针q所指向的对象,q必须指向new分配的内存,且能转换为T*类型shared_ptr p(u)p从unique_ptr u那里接管了对象的所有权,将u置为空shared_ptr p(q,d)p接管了内置指针q所指向的对象的所有权,q必须能转换为T*类型,p将使用可调用对象d来代替deleteshared_ptr p(p2,d)p是shared_ptr p2的拷贝,唯一的区别是p将用可调用对象
13、d来代替deletep.reset()当智能指针中有值的时候,调用reset()会使引用计数减1。如果发现此时p的引用计数为0时,则reset会释放p对象。p.reset(q)p.reset(q,d)若传递了可选参数内置指针q,会将p的引用计数减1(当然,如果发现引用计数为0时,则自动释放p所管理的对象),然后令p指向q。若还传递了参数d,将会调用d而不是delete来释放q。2023-11-121515第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针为了正确使用智能指针,必须坚持以下基本规范:为了正确使用智能指针,必须坚持以下基本规范:(1)不使用相同的内置指针值初始化(或
14、)不使用相同的内置指针值初始化(或reset)多)多个个shared_ptr,原因在于,会造成二次销毁。,原因在于,会造成二次销毁。int*p8=new int;shared_ptr p9(p8);shared_ptr p10(p8);/逻辑错误逻辑错误(2)不)不delete get()返回的指针。返回的指针。(3)如果使用)如果使用get()返回的指针,记住当最后一个对返回的指针,记住当最后一个对应的智能指针销毁后,该指针就变为无效了。应的智能指针销毁后,该指针就变为无效了。:2023-11-121616第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针为了正确使用智能指针
15、,必须坚持以下基本规范:为了正确使用智能指针,必须坚持以下基本规范:(4)默认情况下,一个用来初始化智能指针的普通指)默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用针必须指向动态内存,因为智能指针默认使用delete来释放它所关联的对象。也可以把智能指针绑定到其来释放它所关联的对象。也可以把智能指针绑定到其他类型的指针上,但是我们必须提供自己的删除操作他类型的指针上,但是我们必须提供自己的删除操作来替代来替代delete。(5)避免循环引用。智能指针最大的一个陷阱是循环)避免循环引用。智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。解决方法是改用引
16、用,循环引用会导致内存泄漏。解决方法是改用weak_ptr。见下面的例。见下面的例3-16。:2023-11-1217172、unique_ptrv一个一个unique_ptr“拥有拥有”它所指向的对象,与它所指向的对象,与shared_ptr不同,某个时刻只能有一个不同,某个时刻只能有一个unique_ptr指指向一个给定的对象,向一个给定的对象,unique_ptr被销毁时,其所指向的被销毁时,其所指向的对象也被销毁了。对象也被销毁了。v定义一个定义一个unique_ptr指针时,必须将其绑定到一个指针时,必须将其绑定到一个new返回的指针上。类似返回的指针上。类似shared_ptr,初
17、始化,初始化unique_ptr必须采用直接初始化形式。必须采用直接初始化形式。/p1为指向一个为指向一个double类型的类型的unique_ptr unique_ptr p1;/p2为指向一个值为为指向一个值为10的的int类型的类型的unique_ptr unique_ptr p2(new int(10);第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1218182、unique_ptrv由于一个由于一个unique_ptr拥有它指向的对象,因此拥有它指向的对象,因此unique_ptr不支持普通的不支持普通的复制复制或赋值操作或赋值操作unique_
18、ptr p1(new string(good);unique_ptr p2(p1);/错误,不支持错误,不支持复制复制操作操作unique_ptr p3;p3=p2;/错误,不支持赋值操作错误,不支持赋值操作第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1219192、unique_ptrv但可以通过但可以通过release和和reset方法将指针的所有权从一方法将指针的所有权从一个个(非非const)unique_ptr转移给另一个转移给另一个unique_ptr。/p1放弃所有权,转交给放弃所有权,转交给p2,release将将p1置空置空unique_
19、ptr p2(p1.release();unique_ptr p3(new string(better);/p3放弃所有权,转交给放弃所有权,转交给p2p2.reset(p3.release();/reset释放了释放了p3原来指向的内原来指向的内存存注意:注意:release()并不会释放内存只会转交拥有权,并不会释放内存只会转交拥有权,reset()可以释放内存。可以释放内存。第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1220202、unique_ptrv不能不能复制复制unique_ptr的规则有一个例外:可以的规则有一个例外:可以复制复制或或赋值
20、一个将要被销毁的赋值一个将要被销毁的unique_ptr。v从函数返回一个从函数返回一个unique_ptr:unique_ptr clone(int p)/正确:从正确:从int*创建一个创建一个unique_ptr return unique_ptr(new int(p);第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1221212、unique_ptrv不能不能复制复制unique_ptr的规则有一个例外:可以的规则有一个例外:可以复制复制或或赋值一个将要被销毁的赋值一个将要被销毁的unique_ptr。v还可以返回一个局部对象的还可以返回一个局部对象
21、的复制复制:unique_ptr clone(int p)unique_ptr ret(new int(p);/正确正确 return ret;第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1222222、unique_ptrvunique_ptr中的删除器:类似中的删除器:类似shared_ptr,unique_ptr默认情况下用默认情况下用delete释放它指向的对象。与释放它指向的对象。与shared_ptr一样,可以重载一个一样,可以重载一个unique_ptr中默认的中默认的删除器。但是,删除器。但是,unique_ptr管理删除器的方式与管理删除
22、器的方式与shared_ptr不同,在构建时必须提供删除器的类型。不同,在构建时必须提供删除器的类型。第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1223233、weak_ptrvweak_ptr是一种不控制所指向对象生存期的智能指针是一种不控制所指向对象生存期的智能指针,它指向一个,它指向一个shared_ptr管理的对象。将一个管理的对象。将一个weak_ptr绑定到绑定到shared_ptr不会改变不会改变shared_ptr的引的引用计数。一旦最后一个指向对象的用计数。一旦最后一个指向对象的shared_ptr被销毁,被销毁,对象就会被释放,即使有
23、对象就会被释放,即使有weak_ptr指向对象,对象还指向对象,对象还是会被释放。是会被释放。v当我们创建一个当我们创建一个weak_ptr时,要用一个时,要用一个shared_ptr来初始化它:来初始化它:auto p=make_shared_ptr(10);/wp弱共享弱共享p,p的引用计数未改变的引用计数未改变weak_ptr wp(p第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1224243、weak_ptrv由于对象可能不存在,我们不能使用由于对象可能不存在,我们不能使用weak_ptr直接访直接访问对象,而必须调用问对象,而必须调用lock()
24、。此函数检查。此函数检查weak_ptr指向指向的对象是否存在。如果存在,的对象是否存在。如果存在,lock则返回一个指向共享则返回一个指向共享对象的对象的shared_ptr,同时该对象的引用计数会增加。,同时该对象的引用计数会增加。if(shared_ptr np=wp.lock()vshared_ptr智能指针最大的一个陷阱是循环引用,循智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。解决方法是改用环引用会导致内存泄漏。解决方法是改用weak_ptr。第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1225253、weak_ptr第第3章章 类
25、和对象类和对象-动态内存与智能指针动态内存与智能指针【例3-16】shared_ptr智能指针循环引用问题。#include#include using namespace std;struct Node int _data;shared_ptr next;shared_ptr prev;int main()shared_ptr sp1(new Node);shared_ptr sp2(new Node);sp1-next=sp2;sp2-prev=sp1;return 0;2023-11-1226264、智能指针和动态数组、智能指针和动态数组v标准库提供了一个可以管理标准库提供了一个可以管理
26、new分配数组的分配数组的unique_ptr版本。为了用一个版本。为了用一个unique_ptr管理动态数管理动态数组,我们必须在对象类型后面跟一对方括号。组,我们必须在对象类型后面跟一对方括号。unique_ptr up(new int10);up.release();/自动用自动用delete 销毁其指针销毁其指针v当当unique_ptr指向数组时,我们不能使用点和箭头运指向数组时,我们不能使用点和箭头运算符,而是用下标来访问数组中的元素:算符,而是用下标来访问数组中的元素:for(size_t i=0;i!=10;+i)upi=i;/为每个元素赋予一个新值为每个元素赋予一个新值第第3
27、章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1227274、智能指针和动态数组、智能指针和动态数组第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针unique_ptr uu指向一个动态分配的数组,数组指向一个动态分配的数组,数组元素类型为元素类型为Tunique_ptr u(p)u指向内置指针指向内置指针p所指向的动态分配所指向的动态分配的数组,的数组,p必须能转换为类型必须能转换为类型T*ui返回返回u拥有的数组中位置拥有的数组中位置i处的对象,处的对象,u必须指向一个数组必须指向一个数组指向数组的unique_ptr支持的操作见表3-5。表
28、3-5 指向数组的unique_ptr2023-11-1228284、智能指针和动态数组、智能指针和动态数组v与与unique_ptr不同,不同,shared_ptr不直接支持管理动态不直接支持管理动态数组,如果希望使用数组,如果希望使用shared_ptr管理一个数组必须提供管理一个数组必须提供一个自定义的删除器:一个自定义的删除器:share_ptr sp(new int10,(int*p)delete p;);/这里使用了这里使用了lambda表达式,详见表达式,详见11.5.5节节sp.reset();/使用我们提供的使用我们提供的lambda释放数组,它使释放数组,它使用用delete for(size_t i=0;i!=10;+i)*(sp.get()+i)=i;/由于由于shared_ptr不直接支持不直接支持动态数组,所以我们不能用下标直接访问元素动态数组,所以我们不能用下标直接访问元素第第3章章 类和对象类和对象-动态内存与智能指针动态内存与智能指针2023-11-1229
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。