C++内存管理(候捷)第一讲 笔记

news/2024/7/7 19:13:06 标签: c++, 笔记, 内存分配

内存分配的每一层面

在这里插入图片描述
applications可以调用STL,里面会有allocator进行内存分配;也可以使用C++ 基本工具primitives,比如new, new[], new(), ::operator new();还可以使用更底层的malloc和free分配和释放内存。最底层的是系统调用,比如HeapAlloc,VirtualAlloc
在这里插入图片描述
在C中,malloc 和 free 是标准库函数,不涉及构造函数和析构函数,只是简单的内存分配和释放

int *ptr = (int *)malloc(sizeof(int));
free(ptr);

在C++中,new 和 delete 不仅仅是内存分配和释放的操作符,还会处理对象的构造和析构

四个层面的基本用法

在这里插入图片描述
malloc和 ::operator new()是完全一样的效果
allocator()是创建一个临时对象来调用非static函数
在这里插入图片描述
__GNUC__版本4.9的分配器

基本构件之一new delete expression上

在这里插入图片描述
由于有分配内存,因此要try catch考虑内存分配失败时如何处理
new的过程:1.分配内存 2.指针转型 3.调用构造函数

在指定内存上创建对象使用placement new:new (pointer) Type(initializer);

void* memory = operator new(sizeof(MyClass)); // 分配内存
MyClass* obj = new (memory) MyClass(/* constructor arguments */); // 在指定内存位置创建对象
void* memory = operator new[](sizeof(MyClass) * 5); // 分配数组内存
MyClass* objArray = new (memory) MyClass[5]; // 在数组内存中创建对象

_callnewh 不是 C++ 标准中的函数,而是可能是用户定义的一个函数。通常情况下,这类函数的名字以 _new_handler 结尾,用于处理内存分配失败的情况。
在 C++ 中,当 new 表达式无法分配所需的内存时,会调用用户指定的 new_handler 函数。new_handler 是一个函数指针,指向一个用户定义的函数,其原型通常为

typedef void (*new_handler)();

这个函数可以尝试释放内存、扩大内存池,或者执行其他操作来尝试解决内存不足的问题。如果 new_handler 能够成功处理内存不足的情况,返回;如果不能处理,可以选择抛出异常或者终止程序

基本构件之一new delete expression中

在这里插入图片描述
使用定位 new 运算符后,必须手动调用对象的析构函数来释放资源,否则可能导致内存泄漏

obj->~MyClass(); // 手动调用析构函数
operator delete(memory); // 手动释放内存

delete的动作:先调用析构函数,然后释放内存。
operator delete里调用free

基本构件之一new delete expression下

在这里插入图片描述
ctor和dtor直接调用的测试

Array new

在这里插入图片描述
cookie记录的是下面一块的长度。malloc分配的时候会额外带上一块cookie的信息,供给free释放
在这里插入图片描述
测试
在这里插入图片描述
vc6下malloc new int[10]内存布局:灰色表示具体数据,橙色是debug模式下添加的内存。上面和最下面的两个0x61(61H)是cookie,记录整体内存分配的大小。61H实际上是60H,表示内存分配的大小,后面1H意思是占用最后一位,表示内存分配出去。浅蓝色的pad表示补齐,填补到16的倍数
在这里插入图片描述
在这里插入图片描述

placement new

在这里插入图片描述
placement new允许我们将对象建构在已经分配好的内存中

Complex* pc = new(buf)Complex(1, 2);这句话会被编译器转换为,分别调用operator new(需要第二个参数,表示位置,这个函数只是传回这个位置,不再分配内存),指针转型,调用构造函数

重载

在这里插入图片描述
new是表达式,不可改变不可重载。会调用 operator new,全局(可重载但少见)或者成员函数(可重载)
在这里插入图片描述
容器里把构造函数和析构函数包装在 construct()和destroy(),内存分配动作allocate()和deallocate()会走入分配器allocator中处理

容器分配内存的一般途径:容器使用分配器,在这里插入图片描述
分配器调用 ::operator new 和 ::operator delete,底层可能调用 malloc 和 free:
在这里插入图片描述
重载全局的::operator new 和::operator delete
在这里插入图片描述
在一个类中重载operator new和operator delete。编译器会自动调用
通常会加static,因为调用的地方通常在创建对象的过程中,无法通过对象来调用一般函数
在这里插入图片描述

重载示例

在这里插入图片描述
在这里插入图片描述
重载示例
有虚函数只是把大小放大了,一个12,一个16
在GNU C++4.9版本中构造是从上到下,析构是从下到上
在这里插入图片描述
使用全局new,delete示例
在这里插入图片描述
placement new的重载第一参数必须是size_t类型,接受类的大小,会传入Foo的大小。其余的参数就是括号里的参数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
平常使用的string,就是个typedef ,define basic_string。重载了operator new。每次创建时也额外分配字符串大小的内存

Per class allocator

在这里插入图片描述
内存池:用malloc分配一大块(内存池),然后分成小块,减少malloc的调用次数。另外减少cookie的用量。
在Screen类中引入一个指针next,它的大小是4B,用于串联链表
delete操作,把指针p回收到单向链表中,放到链表的头指针位置
在这里插入图片描述
左边间隔8表示每个Screen对象内存分配的大小为8B,说明每个Screen分配的时候没有cookie。
右边间隔16,表示每个Screen对象内存分配的大小为16B,这是因为对象分配的时候上下加了cookie,最上面和最下面的cookie大小共为8B

Per class allocator 2

在这里插入图片描述
struct AirplaneRep,由于对齐,5B会变成8B
union 是一种特殊的数据结构,可以看作同一个东西,用不同的名称/不同的角度去看待。
上述例子用指针去看待union时,只看8个字节的前4个字节

通过union借用内存块的前4个字节当作指针
在这里插入图片描述
delete没有free,只是把区块回收到链表,并未把内存还给操作系统,链表可能会越来越长,超过512块

Static allocator

从软件工程的角度看,上面的operator new和operator delete对于不同 类都要重载,明显不是一个好的解法,因此将allocator抽象成一个类。

具体的类进行内存分配的时候,只需要调用allocator即可
在这里插入图片描述
在这里插入图片描述
嵌入式指针embedded pointer:借用A对象所占用的内存空间中的前4个字节,这4个字节用来 链住这些空闲的内存块;但是,一旦某一块被分配出去,那么这个块的 前4个字节 就不再需要。因此类A对象的sizeof必须不小于4字节

上述例子定义一个类型obj,不放在外部,污染全局变量。struct里放了一个指针,它的大小为4个字节。这个指针的值,存着下一个内存的地址。由于这里只需要指针,所以union可以不使用。
在这里插入图片描述
由于上面的CHUNK设置为5,每5个对象的内存空间是连续的(间隔都是一个对象的大小),而每个大块之间是不连续的。

Macro for static allocator

在这里插入图片描述
把allocator的部分拿出来用宏来定义
宏是预处理指令,用于在编译过程中执行文本替换。宏通常通过 #define 关键字定义,并在代码中通过宏名称来调用。它们是一种简单的文本替换机制,可以用于创建常量、函数替代、条件编译等。
在宏定义的末尾使用反斜杠是为了告诉编译器该宏定义将在下一行继续。如果在宏定义的最后一行没有使用反斜杠,那么编译器会认为宏定义结束了

版本1:指针,版本2:embedded pointer,版本3:抽取内存的动作到单一class Allocator 版本4:alloctator通过宏抽取出来
在这里插入图片描述
标准库中的allocator
其中一种分配器有16条自由链表,来应对不同大小的块分配,不同的大小的类对象,分配到不同的链表中

New Handler

在这里插入图片描述
new handler 是一个函数指针,当 new 操作符无法分配所需的内存时,会调用与之关联的 new handler
new handler 是全局的,一旦设置,会在程序的生命周期内一直有效,直到被其他 set_new_handler 覆盖
在这里插入图片描述
new handler的例子
在这里插入图片描述
在这里插入图片描述


http://www.niftyadmin.cn/n/5534838.html

相关文章

Node.js下载及安装详细教程

目录 Node.js安装详细教程 下载安装环境变量配置文件结构配置npm在安装全局模块时的路径和缓存cache的路径测试常见命令 Node.js安装详细教程 👁官网下载地址:Download | Node.js (nodejs.org) 下载速度慢的话 可以使用网盘下载: https://pan.quark.…

docker compose方式部署Zabbix 7.0 LTS

docker compose方式部署 Zabbix 7.0 LTS Zabbix 由几个主要的功能组件组成 zabbix-server 是 Zabbix agent 向其报告可用性、系统完整性信息和统计信息的核心组件。zabbix-agent 部署在被监控目标上,用于主动监控本地资源和应用程序,并将收集的数据发送…

开发常识:命令行终端、库源码、开发环境阶段

目录 命令行终端 集成开发环境(IDE ):有插件校验等限制,成功率低于操作系统 库源码 github上搜 官网 UNPKG托管开源的包 专业名词 环境 开发:本地机 开发和调试 生产:最终部署 测试:…

ios13多窗口(UIWindowScene)学习笔记

ios13引入了UIWindowScene类、UIWindowSceneDelegate协议以便支持多窗口功能,但其适用于ipad,不适用于iphone,因为iphone不支持多窗口功能。注意,这里说的窗口不是UIWindow,而是UIWindowScene。 ios13前后的app的UI架…

Springboot3本地编译exe文件(实现快速启动仅需200ms)

1. 准备好grallvm版本的JDK jdk17以上 (springboot3最低支持jdk17) grallvm-jdk17 Download GraalVM 下载界面 2. 配置maven 3.9.x 及以上 maven 3.9.8 Maven – Download Apache Maven 3.创建SpringBoot项目 3.1 项目所需依赖 记得选择这俩个进…

为什么 npm run serve 正常,npm run build 就报错:digital envelope routines::unsupported

这个错误通常与 Node.js 版本和使用的加密算法有关。让我解释一下原因和可能的解决方案: 错误原因 这个错误(“error:0308010C:digital envelope routines::unsupported”)通常发生在以下情况: 使用较新版本的 Node.js&#xf…

mpeg格式怎么转换成mp4?这四种转换方法非常好用!

mpeg格式怎么转换成mp4?在数字视频领域中,MPEG格式算是相对冷门的一种选择,然而,选择这种格式却不是没有代价的,首先,MPEG采用了有损压缩技术,这意味着在视频处理过程中,会丢失一些细…

RabbitMQ入门教程(精细版二带图)

目录 六 RabbitMQ工作模式 6.1Hello World简单模式 6.1.1 什么是简单模式 6.1.2 RabbitMQ管理界面操作 6.1.3 生产者代码 6.1.4 消费者代码 6.2 Work queues工作队列模式 6.2.1 什么是工作队列模式 6.2.2 RabbitMQ管理界面操作 6.2.3 生产者代码 6.2.4 消费者代码 …