IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ |动态内存 -> 正文阅读

[C++知识库]C++ |动态内存

1. 动态内存和智能指针

C++提供了两种智能指针管理动态对象,负责自动释放所指对象

类似vector,智能指针也是模板。创建一个只能指针时,需要给出指针可以指向的类型

1.1 shared_ptr

在这里插入图片描述
在这里插入图片描述
make_shared定义在头文件memory内。

类似于顺序容器的emplace成员,make_shared使用其参数构造所指向的对象。如果不传递任何参数,对象就会进行值初始化。

1.1.1 shared_ptr计数器

每个shared_ptr会有一个与之关联的计数器,记录着和它指向同一对象的shared_ptr数目。

拷贝shared_ptr时,计数会递增。如 当使用一个shared_ptr初始化另一个shared_ptr,将一个shared_ptr作为参数传递给一个函数,将shared_ptr作为函数返回值。

改变shared_ptr指向对象,一个shared_ptr被销毁,计数器递减。具体的实现为,shared_ptr的析构函数会递减所指对象的引用计数。

当shared_ptr计数器减为0,shared_ptr的析构函数就会自动释放所指的对象。
在这里插入图片描述
在这里插入图片描述

1.1.2 使用动态生存期的资源的类

程序使用动态内存出于三个原因:
在这里插入图片描述
在这里插入图片描述

对于第三种原因,可使用shared_ptr实现数据共享:
需要共享数据(动态内存分配),但是多个共享者的生命周期又不同。希望分配的资源和共享者具有独立的生命周期(shared_ptr计数器机制)。用了shared_ptr,只要有共享者仍然指向该对象,该对象就不会被销毁
在这里插入图片描述

如上所示,用make_shared在堆区分配内存(共享),然后把用shared_ptr存放数据的地址(独立的生命周期)。在拷贝对象时,StrBlob的数据成员data也会被拷贝,当拷贝的某个对象被释放,只要还有对象存在,data的计数就不会为0,这块堆空间也就一直没被释放。

1.2 直接管理内存

1.2.1 动态分配内存new,delete

new:创建对象,返回指针。
delete:销毁指针所指对象,释放对应内存。

1.2.1.1 new

默认情况下,动态分配的对象是默认初始化的。也就是内置类型的对象的值将是没定义的。
在这里插入图片描述
也可以用圆括号进行值初始化,或者使用花括号进行列表初始化:
在这里插入图片描述
在这里插入图片描述
补充:对于定义了构造函数的类而言,值初始化(给出括号但不给初始值-见那一章)是没有意义的,对象实际使用的是默认初始化。而对于内置类型,默认初始化得到未定义值,值初始化会得到一个良好定义的值。

如果使用new时,提供了一个圆括号包裹的初始化器,那么可以使用auto自动推导要分配的类型。使用这种方法要求只能用一个初始化器在这里插入图片描述
可以用new分配const对象除非对象定义了默认构造函数,可以隐式初始化,否则必须显式初始化,new返回的是一个指向常量的指针:
在这里插入图片描述
当程序所分配的空间耗尽时,new会失败,抛出bad_alloc异常,可以改变使用new的方式阻止抛出异常:
在这里插入图片描述
第二中形式的new称为定位new,允许向new传递诸如上文的nothrow的额外参数。

1.2.1.2 delete

传递给delete的指针必须是指向动态分配的内存的指针或者是空指针
尝试释放一块并非new的内存,或者释放同一空间两次(多个指针指向同一空间),都会产生未定义的行为。

可以向释放普通指针一样释放指向常量的指针。

使用常规指针的动态对象的生命周期到被显式释放时为止。(智能指针会随着对象销毁而释放内存)
而一旦常规指针自身生命周期结束,他所申请的动态对象将无法再被释放。所以一定要delete。

delete之后的指针就称为空悬指针,即指向曾经保存数据对象但现在已经无效的内存的指针。空悬指针具有野指针(未初始化的指针)的所有缺点。

如何处理空悬指针?在指针所指向内存释放后将其赋值为nullptr;但是此方法很有限,当有多个指针指向同一内存时,重置一个指针,其他指针还会出错。

1.3 shared_ptr结合new

shared_ptr具有接收普通指针的构造函数。但是该构造函数是explicit的,即不能隐式将普通指针转化为智能指针,需要使用直接初始化来显式初始化智能指针
在这里插入图片描述
也可以使用reset完成指针类型转换:
在这里插入图片描述

用来初始化智能指针的普通指针必须指向动态内存,因为当智能指针最后会默认使用delete释放它所关联的对象。如果使用不指向动态内存的指针初始化智能指针,需要定义自己的释放操作来替代delete。
在这里插入图片描述
在这里插入图片描述

1.3.1 智能指针注意事项

当把shared_ptr绑定到普通指针时,就将管理内存的责任交给了shared_ptr,不要再通过普通指针访问对象。如下:
在这里插入图片描述
这种行为是很危险的。
不要使用get的返回值初始化另一个智能指针或为智能指针赋值。get返回一个指向智能指针所指对象的内置指针。
在这里插入图片描述

1.4 智能指针和异常

智能指针在发生异常时也能完成内存的释放。
在这里插入图片描述

而如果是直接管理内存的方式,则不能释放内存:
在这里插入图片描述
为C和C++两种语言设计的类,通常要求用户显式释放所使用的任何资源,即分配了资源,但是没有析构函数,如果忘记释放,也会造成资源泄露。
可以使用改造版的shared_ptr帮忙自动释放资源。默认情况下,shared_ptr假定指向动态内存,当计数清0时,对所管理的对象执行delete操作。只要写一个针对性的资源释放函数(称为删除器函数)来代替delete,就能使用shared_ptr自动释放资源。
比如对如下C和C++都可使用的网络库操作:

在这里插入图片描述
定义删除器,用于关闭网络释放资源:
在这里插入图片描述
创建一个改造后的shared_ptr指向connection的对象:
在这里插入图片描述

1.5 unique_ptr

1.5.1 unique_ptr基础

unique_ptr只能绑定到内置指针上。
unique_ptr拥有他所指向的对象,所以不支持普通的拷贝和赋值操作。一个时刻只能有一个unique_ptr指向一个给定对象,当该unique_ptr被销毁时,所指向的对象也被销毁。
在这里插入图片描述
虽然unique_ptr不支持普通的赋值和拷贝操作,但可以使用release或者reset将指针所有权从一个unique_ptr转移到另一个:
在这里插入图片描述
注意release以后一定要接收结果,或是重新绑定至智能指针,或是直接管理返回的内置指针。否则将导致指针和它所指的对象丢失。

可以拷贝和赋值一个将要被销毁的unique_ptr对象。
return临时对象:
在这里插入图片描述
或者return局部遍历:
在这里插入图片描述
在这里插入图片描述

1.5.2 重载默认删除器

默认情况下,unique_ptr以delete作为默认删除器。
和shared_ptr不同,如果重载了unique_ptr的删除器,在构造该类型的对象时需要在尖括号内unique_ptr指向类型之后提供删除器类型(类似重载关联函数的比较操作)。如下:
在这里插入图片描述

1.6 weak_ptr

weak_ptr不控制所指对象的生存周期,指向由shared_ptr管理的对象不会增加引用计数。
在这里插入图片描述
由于weak_ptr所指对象可能不存在(被释放),所以不能直接用weak_ptr访问对象,需要调用lock。

weak_ptr不会影响对象生命周期,也可以处理对象为空的情况。

2. 动态数组

2.1 new

前面提到用new动态创建对象,现在使用new动态创建一个对象的数组。
在这里插入图片描述
使用[]在new时指定分配的对象数目:
在这里插入图片描述
new返回的是一个指向元素的指针,而不是数组。所以不能对返回值调用beginend,同样的也不能使用范围for循环

2.1.1 初始化动态数组

值初始化:
在这里插入图片描述
列表初始化,当初始值数目小于元素数目,剩下的元素将被值初始化,如果初始值数目大于元素数目,将抛出bad_array_new_length异常:
在这里插入图片描述
由于不能用括号包裹初始化器进行初始化,所以不能用auto分配数组。

当试图将动态数组元素数量设置为0时,代码能正常工作,会得到一个合法的非空指针,可以用该指针加上0,或者减去自身。但无法访问
在这里插入图片描述

2.1.2 释放动态数组

释放动态数组和释放单个元素不同,要加上方括号:
在这里插入图片描述
先销毁pa的元素,再释放内存,元素按逆序销毁。
使用数组的类型别名创建动态数组时,new表达式不需要[]。但是删除时,无论如何都要带上[]
在这里插入图片描述

2.1.3 智能指针和动态数组

标准库提供了可以管理new分配数组的unique_ptr版本,定义该unique_ptr时,在类型名后跟上[]
在这里插入图片描述
由于up指向数组,所以当unique_ptr被释放时,会调用delete[]
在这里插入图片描述
和unique_ptr不同,shared_ptr不支持直接管理动态数组,如果一定要使用shared_ptr管理动态数组,需要提供自己定义的删除器,如果不提供删除器,默认会使用delete去销毁一个动态数组,这是未定义的行为:
在这里插入图片描述
即使用改变了删除器的版本去管理动态数组,也会有问题:
在这里插入图片描述
在这里插入图片描述

2.2 allocator类

使用new可能会造成空间浪费。new将分配内存和构造对象组合在一起,可能创建了一些永远也用不到的对象,还会导致没有默认构造函数的类无法动态分配数组
在这里插入图片描述
如上,分配了n个string对象的空间,但可能只用了前几个,就被delete[]了。

allocator分配的内存是原始的、未构造的。
在这里插入图片描述
在这里插入图片描述
allocate只是分配了这么大的空间,里面什么都没有:
在这里插入图片描述
没有构造的原始内存是不能使用的。需要调用construct构造后才能使用:
在这里插入图片描述
用完对象后需要对每个对象调用destrory完成对象销毁。
在这里插入图片描述
销毁完对象的空间可以再次使用,或者释放内存(全部释放,需要把所有构造的对象destroy):
在这里插入图片描述
allocator可以拷贝和填充未被初始化的内存(但是需要先被allocate分配):
在这里插入图片描述
uninitialized_copy返回最后一个构造的元素之后的元素。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:08:08  更:2022-04-01 23:09:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:31:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码