第十二章 动态内存 426

12-1动态内存与智能指针
动态内存的管理通过一对运算符来完成:
new:在动态内存中为对象分配内存空间并返回一个指向该对象的指针,可以选择对对象初始化
delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存
两种智能指针:
shared_ptr允许多个指针指向同一个对象
unique_ptr“独占”所指的对象
标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象
三种类型都定义在头文件memory中
1、shared_ptr类
智能指针也是模板,与vector类似:
在这里插入图片描述
在这里插入图片描述
make_shared函数
make_shared函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr,定义在头文件memory中
make_shared的定义方式与模板类相同:函数名后跟一个尖括号,尖括号中指出类型
在这里插入图片描述
用auto更简单:
在这里插入图片描述
shared_ptr的拷贝和赋值
shared_ptr都有一个关联的计数器,通常称其为引用计数
当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其它的shared_ptr指向相同的对象:
在这里插入图片描述
无论何时拷贝一个shared_ptr,计数器都会递增;给shared_ptr赋予一个新值或shared_ptr被销毁,计数器就会递减:在这里插入图片描述
当指定对象的最后一个shared_ptr被销毁时,shared_ptr类会通过析构函数自动销毁此对象
类似于构造函数,每个类都有一个析构函数,它控制此对象销毁时做的操作
shared_ptr的析构函数会递减它所指对象的引用计数,如果引用计数变为0,shared_ptr的析构函数会销毁对象,并释放它占用的内存
在这里插入图片描述
调用factory并将factory返回的指针保存在局部变量p中:
在这里插入图片描述
由于p是局部变量,在函数结束后它将被销毁
使用了动态生存期的类
在这里插入图片描述
在这里插入图片描述
本节之前我们使用过的类,分配的资源都与对应对象生存期一致
例如:
在这里插入图片描述
Blob对象的不同拷贝之间共享相同的元素,即:当我们拷贝一个Blob时,原Blob对象和拷贝应引用相同的底层元素
如果两个对象共享底层的数据,当某个对象被销毁时,不能单方面地销毁底层数据
在这里插入图片描述
定义StrBlob类
StrBlob:管理string的类
如果一个vector保存在一个Blob类元素b2中,当b2离开其作用域时,此vector会被销毁
希望保存vector中的元素,我们将它保存在动态内存中
为实现数据共享,我们为每个StrBlob设置一个shared ptr来管理动态分配的vector,此shared ptr记录有多少个StrBlob共享相同的vector,并在vector的最后一个使用者被销毁时释放vector
这个类有一个默认构造函数和一个构造函数,接受单一的initializer_list类型参数
在这里插入图片描述
StrBlob的构造函数
在这里插入图片描述
构造函数将其参数传给对应的vector,通过列表中的值初始化vector中的元素
元素访问成员函数
StrBlob定义了一个名为check的private函数,用于检查给定的索引是否在合法范围内
在这里插入图片描述
如果check成功,则继续利用底层vector的操作来完成自己的工作:
在这里插入图片描述
StrBlob的拷贝、赋值和销毁
类似Sales data类,StrBlob使用默认版本的拷贝、赋值和销毁成员函数来对其对象进行操作
StrBlob只有一个shared ptr类型的数据成员,所以StrBlob的对象被拷贝、赋值和销毁时,它的shared ptr会被拷贝、赋值和销毁
对于由StrBlob构造函数分配的vector,当最后一个指向它的StrBlob对象被销毁时,它会随之被自动销毁
2、直接管理内存
使用new动态分配和初始化对象
在这里插入图片描述
在这里插入图片描述
对动态分配的对象进行值初始化:
在这里插入图片描述
对类类型来说,值初始化没有什么意义,因为它有默认构造函数
而对内置类型来说,默认初始化的对象的值是未定义的
如果提供了一个括号包围的初始化器,则可以使用auto从此初始化器来推断我们要分配对象的类型
当括号中仅有单一初始化器是才能使用auto:
在这里插入图片描述
动态分配的const对象
在这里插入图片描述
一个动态分配的const对象必须进行初始化,对于有默认构造函数的类类型,可以隐式初始化
返回的指针是一个指向const的指针
内存耗尽
如果new不能分配所要求的内存空间,它会抛出一个类型为bad_alloc类型的异常
阻止它抛出异常:
在这里插入图片描述
这种形式的new为定位new
定位new表达式允许我们向new传递额外的参数
将nothrow传递给new是告诉它不能抛出异常,如果这个new不能分配内存,则返回一个空指针
bad_alloc和nothrow都定义在头文件new中
释放动态内存
在这里插入图片描述
delete表达式接受一个指针,销毁给定指针指向的对象,并释放对应的内存
指针值和delete
传给delete的指针必须指向动态分配的内存,或者是一个空指针
释放一块并非new分配的内存,或将相同的指针释放多次,都会引发未定义的结果:
在这里插入图片描述
一个const对象的值不能改变,但它是可以销毁的:
在这里插入图片描述
动态对象的生存期直到被释放为止
由shared ptr管理的内存在最后一个shared ptr销毁时会被自动释放
对于一个由内置类型管理的动态对象,直到被显式释放之前它都是存在的
factory函数:
在这里插入图片描述
在这里插入图片描述
调用factory,但内存未释放:
在这里插入图片描述
释放p指向的那块内存:
在这里插入图片描述
delete一个指针后,指针值变为无效,但指针仍保存着已经释放了的内存的地址,这样的指针称为空悬指针
避免空悬指针的方法:在指针即将离开其作用域之前释放掉它所关联的内存
如果指针需要保留,则将nullptr赋予指针
在这里插入图片描述
3、shared_ptr和new结合使用
如果我们不初始化一个智能指针,它将被初始化为一个空指针
可以用new返回的指针初始化智能指针:
在这里插入图片描述
不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式:
在这里插入图片描述
同理,一个返回智能指针的函数不能在返回语句中隐式转换一个普通指针:
在这里插入图片描述
正确形式:
在这里插入图片描述
初始化智能指针的普通指针必须指向动态内存
如果将智能指针绑定到指向其它类型资源的指针上,就必须自己提供操作代替delete
在这里插入图片描述
shared ptr可以协调对象的析构,但仅限于自身(shared ptr)之间,所以推荐使用make shared而不是new
在这里插入图片描述
传递给它一个shared ptr:
在这里插入图片描述
传给process一个内置指针:
在这里插入图片描述
不要使用get初始化另一个智能指针或为智能指针赋值
智能指针定义了一个名为get的函数,它返回一个内置指针,指向智能指针管理的对象
get用于我们需要向不能使用智能指针的代码传递一个内置指针的情况
使用get返回的代码不能delete此指针
在这里插入图片描述
其它shared ptr操作
reset用于将一个新的指针赋予一个shared ptr:
在这里插入图片描述
reset也会更新引用计数
reset常与unique一起使用,来控制多个shared ptr共享的对象
在改变底层对象之前,我们检查自己是否是当前对象仅有的用户,如果不是,在改变之前制作一份新的拷贝
4、智能指针和异常
使用智能指针,即使程序块过早结束,智能指针类也能确保内存在不需要时被释放:
在这里插入图片描述
不论是程序结束还是发生异常局部对象都会被销毁
在这里插入图片描述
智能指针和哑类
没有定义析构函数释放资源的类常常被遗忘释放内存在这里插入图片描述
connection没有析构函数,要使用shared ptr来确保connection被正确关闭
为了用shared ptr来管理一个connection,要定义一个函数来代替delete
这个删除器函数完成对shared ptr中保存的指针进行释放操作:
在这里插入图片描述
创建一个shared ptr时,可以传递一个指向删除器函数的参数:
在这里插入图片描述
在这里插入图片描述
5、unique_ptr
一个unique ptr拥有它所指向的对象,某个时刻只能有一个unique ptr指向一个给定对象
当unique被销毁时,它所指向的对象也被销毁
没有类似make shared的标准库函数返回一个unique ptr
unique ptr只能被直接初始化
在这里插入图片描述
unique不支持普通的拷贝或赋值操作:
在这里插入图片描述
在这里插入图片描述
通过调用release或reset将指针的所有权从一个非const的unique ptr转移给另一个unique ptr:
在这里插入图片描述
release返回unique当前保存的指针并将其置为空
p2保存原来p1保存的指针,p1被置为空
reset释放了p2指向的内存,p3被置为空,p2保存p3保存的指针
在这里插入图片描述
在这里插入图片描述
传递unique ptr和返回unique ptr
可以拷贝或赋值一个将要被销毁的unique ptr
从函数返回一个unique ptr:
在这里插入图片描述
返回一个局部对象的拷贝:
在这里插入图片描述
向unique ptr传递删除器
在这里插入图片描述
用unique ptr重写连接程序:
在这里插入图片描述
6、weak_ptr
weak_ptr是一种不控制所指对象生存期的智能指针,指向由一个shared ptr管理的对象
weak ptr不会改变shared ptr的引用计数
一旦最后一个指向对象的shared ptr被销毁,即使有weak_ptr,对象还是会被释放
在这里插入图片描述
在这里插入图片描述
由于对象可能不存在,我们不能使用weak_ptr直接访问对象,必须调用lock检查对象是否存在
若存在,lock返回一个指向共享对象的shared_ptr:
在这里插入图片描述
核查指针类
我们为StrBlob定义一个伴随指针类,将其命名为StrBlobPtr,它保存一个weak ptr,指向StrBlob的data成员
在这里插入图片描述
在这里插入图片描述
指针操作
定义deref和incr函数,分别用来解引用和递增StrBlobPtr
在这里插入图片描述
在这里插入图片描述
为了访问data成员,指针类必须声明为StrBlob的friend
为StrBlob定义begin和end操作,返回一个指向它自身的StrBlobPtr:
在这里插入图片描述
12-2动态数组
1、new和数组
让new分配一个动态数组时,要在类型名之后跟一对方括号,在其中指明要分配的对象的数目
new成功分配要求数量的对象后会返回指向第一个对象的指针:
在这里插入图片描述
方括号中的数不必是常量,但必须是整型
使用表示数组类型的类型别名,new表达式中不需要带方括号:
在这里插入图片描述
动态数组并不是数组类型,不能对动态数组调用begin和end,也不能使用范围for语句来处理动态数组中的元素
初始化动态分配对象的数组
默认情况下,new分配的对象是默认初始化的
对动态数组中的元素进行值初始化,要在大小之后跟一对空括号:
在这里插入图片描述
还可以提供一个元素初始化器的花括号列表:
在这里插入图片描述
如果初始化器数目大于元素数目,new表达式失败,不分配任何内存,并抛出一个类型为bad_array_new_length的异常,类似bad_alloc,此类型定义在头文件new中
不能在括号中给出初始化器,不能用auto分配数组
动态分配一个空数组是合法的
在这里插入图片描述
如果get_size返回0,代码仍能正常工作:
在这里插入图片描述
new返回一个合法的非空指针,类似于长度为0的数组的尾后迭代器,它不能解引用
释放动态数组
在这里插入图片描述
数组中的元素按逆序销毁
当释放指向数组的指针时,一定要加上空方括号;如果忽略了空方括号,其行为是未定义的
用类型别名定义数组类型时,new表达式中不用空方括号,但释放数组时,空方括号不能省:
在这里插入图片描述
智能指针和动态数组
在这里插入图片描述
当一个unique_ptr指向一个数组时,点和箭头运算符都不能使用,可以使用下标运算符访问数组中的元素:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
shared_ptr不支持下标运算符,并且它不支持指针算术运算,必须用get获取一个内置指针,用它来访问数组元素:
在这里插入图片描述
2、allocator类
在这里插入图片描述
可能用不到n个string,并且每个使用到的元素都被赋值了两次:默认初始化时;赋值时
allocator类
allocator分配的内存是原始的未构造的
为了定义一个allocator对象,必须指明这个allocator可以分配的对象类型:
在这里插入图片描述
在这里插入图片描述
allocator分配未构造的内存
construct成员函数接受一个指针和0个或多个额外参数,在给定位置构造一个元素:
在这里插入图片描述
在这里插入图片描述
用完对象后,必须对每个构造的元素调用destroy来销毁它们
destroy接受一个指针,对指向的对象进行析构函数:
在这里插入图片描述
释放内存通过调用deallocate来完成:

在这里插入图片描述
在这里插入图片描述
拷贝和填充未初始化内存的算法
在这里插入图片描述
将一个vector中的内容拷贝到动态内存中,分配一块比vector中元素所占空间大一倍的内存,前半部分保存vector中的元素,后半部分用一个给定值填充:
在这里插入图片描述
3、使用标准库:文本查询程序
输出样例:
在这里插入图片描述
程序要求:
在这里插入图片描述
实现要求:
在这里插入图片描述
使用TextQuery类
函数接受一个指向要处理文件的ifstream并与用户交互,打印给定单词的查询结果:
在这里插入图片描述
在这里插入图片描述
TextQuery构造函数
在这里插入图片描述
Queryresult类
Queryresult类的构造函数:
在这里插入图片描述
query函数
在这里插入图片描述
打印结果
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值