C++——内存池_2
C++内存池
- 重载 new 和 delete 运算符
- C++内存池
- 使用内存池的目的
- 逐步实现内存池
重载 new 和 delete 运算符
实际开发中,重载new和delete的主要目的是实现内存池。内存池在高性能的服务程序中很常用。点击浏览重载 new 和 delete 运算符的内容,建议先看这部分。
C++内存池
内存池是预先分配的内存空间。程序启动的时候,先向系统申请一大块连续的空间。在程序中,如果需要用内存, 从内存池中借一块小的空间出来,用完了再还回去。说白了,如果沒有内存池,直接向系统借;如果有内存池,就向内存池借。
程序在运行的过程中,如果内存池太小了,可以扩展。但有一个原则,每次都会向系统申请一大块连续的内存空间。
使用内存池的目的
目的主要有两个:
- 提升分配和归还的速度
- 对高性能的服务程序来说,频繁的new和delete也算浪费资源。
- 减少内存碎片
- 碎片多了,系统管理内存的效率就会下降,系统的性能也会下降。
逐步实现内存池
#include <iostream>using namespace std;class Car
{public:int speed;float acceleration;Car(int sp, float acc){speed = sp, acceleration = acc; cout << "~~ 类的构造 speed: " << speed << " acceleration: " << acceleration << " ~~" << endl;}~Car() {cout << "~~ 调用析构 ~~" << endl;}
};int main() {Car* p = new Car(320, 4.6);cout << "p的地址:" << p << "\nspeed: " << p->speed << " acceleration: " << p->acceleration << endl;delete p;return 0;
}
在上面这段代码中,Car类有两个类型分别是int和float的成员变量,所以,用Car类创建的对象占用的内存空间是8字节。
我打算预先向系统申请连续的18字节的内存空间,如下图:

也就是说,内存池的大小是18字节。
组织数据的方法是:标志位,1个字节(图中的0号、9号字节),表示后面的空间是否被占用。后面8个(1~8号、10~17号)字节刚好可以存放1个Car类对象。
标志位的取值只有0和1,0表示空闲,1表示占用。故这18个字节的内存池,能存放2个Car对象。
当需要为Car对象分配内存的时候,先判断标志位是否被占用,若没有被占用,就把标志位后面的这个内存地址返回。
了解以上这些知识后,来看一段示例代码:
#include <iostream>using namespace std;class Car
{public:int speed;float acceleration;Car(int sp, float acc){speed = sp, acceleration = acc; cout << "~~ 类的构造 speed: " << speed << " acceleration: " << acceleration << " ~~" << endl;}~Car() {cout << "~~ 调用析构 ~~" << endl;}void* operator new(size_t size){cout << "调用类的重载 new: " << size << " 字节" << endl;void* ptr = malloc(size);cout << "申请的地址是:" << ptr << endl;return ptr;}void operator delete(void* ptr){cout << "调用了类的重载 delete" << endl;if (ptr == 0) return;free(ptr);}
};int main() {Car* p = new Car(320, 4.6);cout << "p的地址:" << p << "\nspeed: " << p->speed << " acceleration: " << p->acceleration << endl;delete p;return 0;
}
上面这段代码在C++——内存池_1也用到了,现在在此基础上实现简单的内存池。
首先,内存池需要一个指针来存放它的起始地址。内存池就是一块内存,没有数据类型的说法,用什么类型的指针都可以。在此示例中,用字符更合适,因为字符指针+1就是偏移1个字节。
- 声明指针,用于存放内存池的起始地址;
- 定义初始化内存池的函数;
- 定义释放内存池的函数
接下来修改分配内存的重载函数void* operator new(size_t size),向内存池中申请内存。池中只有2个位置,判断标志位,哪个空闲就返回哪一块。如果没有空闲维位置,可以返回空地址,也可以直接向系统申请内存。
修改申请内存的重载函数,释放内存的函数void operator delete(void* ptr)也一并在下方给出,代码如下:
#include <iostream>
#include <cstring>using namespace std;class Car
{public:int speed;float acceleration;static char* m_pool; // 内存池的起始地址(为什么是静态变量?后面有提到)static bool initpool() // 初始化内存池的函数{m_pool = (char*)malloc(18); // 向系统申请18字节的内存if (m_pool == 0) return false; // 若申请失败,返回 falsememset(m_pool, 0, 18); // 把内存池中的内容初始化为 0cout << "内存池的起始地址:" << (void*)m_pool << endl;return true;}static void freepool() // 释放内存池的函数{if(m_pool == 0) return; // 如果内存池为空,无需释放,直接返回free(m_pool); // 把内存池归还给系统cout << "内存池已释放" << endl;}Car(int sp, float acc){speed = sp, acceleration = acc; cout << "~~ 类的构造 speed: " << speed << " acceleration: " << acceleration << " ~~" << endl;}~Car() {cout << "~~ 调用析构 ~~" << endl;}// 类的静态成员函数 static 所以要把 m_pool 声明为静态变量 初始化函数和释放函数也要声明为 static// C++——内存池_1 这篇文章的最后有讲到这个点void* operator new(size_t size){if(m_pool[0] == 0) // 判断第一位是否空闲 也就是要看内存空间 m_pool 的第一位的标志位 m_pool[0] 是否等于 0{cout << "分配了第一块内存:" << (void*)(m_pool + 1) << endl;m_pool[0] = 1; // 把第一位置标记为已分配return m_pool + 1; // 返回第一个用于存放对象的地址}if(m_pool[9] == 0) // 判断第二位是否空闲{cout << "分配了第二块内存:" << (void*)(m_pool + 10) << endl;m_pool[9] = 1; // 把第一位置标记为已分配return m_pool + 10; // 返回第一个用于存放对象的地址}// 若两个位置都不可用,就直接向系统申请内存void* ptr = malloc(size); // 向系统申请内存cout << "向系统申请到的内存地址:" << ptr << endl;return ptr;}void operator delete(void* ptr){if(ptr == 0) return; // 若传进来的地址为空,直接返回。if(ptr == m_pool + 1) // 若传进来的地址是内存池的第一个位置{cout << "释放第一块内存!" << endl;m_pool[0] = 0; // 把第一个位置标记为空闲return; // 归还内存之后,函数应该立即返回}if(ptr == m_pool + 10) {cout << "释放第二块内存!" << endl;m_pool[9] = 0; // 把第二个位置标记为空闲return; // 归还内存之后,函数应该立即返回}// 若传进来的地址不属于内存池,把它归还系统。free(ptr); // 释放内存}
};// 内存池的指针 m_pool 是静态成员变量,需要在 main 函数外面初始化。
char* Car::m_pool = 0; // 初始化内存池指针int main() {if(Car::initpool() == false) {cout << "初始化内存池失败!!!" << endl; return -1;}Car* p1 = new Car(320, 4.6);cout << "p1的地址:" << p1 << "\nspeed: " << p1->speed << " acceleration: " << p1->acceleration << endl;Car* p2 = new Car(220, 4.0);cout << "p2的地址:" << p2 << "\nspeed: " << p2->speed << " acceleration: " << p2->acceleration << endl;Car* p3 = new Car(520, 2.9);cout << "p3的地址:" << p3 << "\nspeed: " << p3->speed << " acceleration: " << p3->acceleration << endl;delete p1;Car* p4 = new Car(420, 4.4);cout << "p4的地址:" << p4 << "\nspeed: " << p4->speed << " acceleration: " << p4->acceleration << endl;delete p2;delete p3;delete p4;Car::freepool(); // 释放内存池return 0;
}
如果内存池用完了,根据业务的需求,一般有三种方法:
- 扩展内存
- 直接向系统申请内存
- 返回空地址
编译运行,效果应该如下(地址是你PC上开辟的内存地址,与我的不同):
内存池的起始地址:0x56366e051eb0
分配了第一块内存:0x56366e051eb1
~~ 类的构造 speed: 320 acceleration: 4.6 ~~
p1的地址:0x56366e051eb1
speed: 320 acceleration: 4.6
分配了第二块内存:0x56366e051eba
~~ 类的构造 speed: 220 acceleration: 4 ~~
p2的地址:0x56366e051eba
speed: 220 acceleration: 4
向系统申请到的内存地址:0x56366e052ee0
~~ 类的构造 speed: 520 acceleration: 2.9 ~~
p3的地址:0x56366e052ee0
speed: 520 acceleration: 2.9
~~ 调用析构 ~~
释放第一块内存!
分配了第一块内存:0x56366e051eb1
~~ 类的构造 speed: 420 acceleration: 4.4 ~~
p4的地址:0x56366e051eb1
speed: 420 acceleration: 4.4
~~ 调用析构 ~~
释放第二块内存!
~~ 调用析构 ~~
~~ 调用析构 ~~
释放第一块内存!
内存池已释放
感谢浏览,一起学习!
相关文章:
C++——内存池_2
C内存池 重载 new 和 delete 运算符C内存池使用内存池的目的逐步实现内存池 重载 new 和 delete 运算符 实际开发中,重载new和delete的主要目的是实现内存池。内存池在高性能的服务程序中很常用。点击浏览重载 new 和 delete 运算符的内容,建议先看这部…...
如何使用PHP爬虫获取店铺详情:一篇详尽指南
在数字化时代,数据的价值不言而喻。对于企业来说,获取竞争对手的店铺详情、顾客评价等信息对于市场分析和决策至关重要。PHP作为一种广泛使用的服务器端脚本语言,结合其强大的库支持,使得编写爬虫程序变得简单而高效。本文将详细介…...
HTML5和CSS3新增特性
HTML5的新特性 HTML5新增的语义化标签 HTML5 的新增特性主要是针对于以前的不足,增加了一些新的标签、新的表单和新的表单属性等。 这些新特性都有兼容性问题,基本是 IE9 以上版本的浏览器才支持,如果不考虑兼容性问题,可以大量…...
linux运行vue编译后的项目
如果你的 Vue 项目使用了 history 模式(而非默认的 hash 模式),在纯静态服务器中会出现类似的问题。因为 Vue Router 的 history 模式要求所有未匹配的路径都重定向到 index.html,以便 Vue 前端处理路径。 首先在本地执行npm run…...
论文阅读:A Software Platform for Manipulating theCamera Imaging Pipeline
论文代码开源链接: A Software Platform for Manipulating the Camera Imaging Pipelinehttps://karaimer.github.io/camera-pipeline/摘要:论文提出了一个Pipline软件平台,可以方便地访问相机成像Pipline的每个阶段。该软件允许修改单个模块…...
【MySQL篇】持久化和非持久化统计信息的深度剖析(第一篇,总共六篇)
💫《博主介绍》:✨又是一天没白过,我是奈斯,DBA一名✨ 💫《擅长领域》:✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌️…...
Ubuntu下安装Qt
1.如图1所示,在Index of /archive上下载安装包; 图1 2.将图1安装包下载好之后,通过共享文件夹的方式拷贝到ubutntu,如图2所示; 图2 3.如图3所示,执行chmod x qt-creator-opensource-linux-x86_64-10.0.2.…...
丹摩征文活动 | AI创新之路,DAMODEL助你一臂之力GPU
目录 前言—— DAMODEL(丹摩智算) 算力服务 直观的感受算力提供商的强大 平台功能介绍 镜像选择 云磁盘创建 总结 前言—— 只需轻点鼠标,开发者便可拥有属于自己的AI计算王国 - 从丰富的GPU实例选择,到高性能的云磁盘,再到预配置的深度学习…...
数据库(总结自小林coding)|索引失效的场景、慢查询、原因及如何优化?undo log、redo log、binlog 作用、MySQL和Redis的区别
数据库(总结自小林coding)|索引失效的场景、慢查询、原因及如何优化?undo log、redo log、binlog 作用、MySQL和Redis的区别 说一下索引失效的场景?什么是慢查询?原因是什么?可以怎么优化?undo …...
Docker容器运行CentOS镜像,执行yum命令提示“Failed to set locale, defaulting to C.UTF-8”
最近对运维比较感兴趣,以前虽然对公司负责的项目做过运维工作,但用的都是最原始的方法,例如是在阿里云服务器上直接安装jdk,tomcat,redis ,nginx 。这种方式对不大的项目还能够支持,随着项目变大,服务增加&…...
OpenCV基本图像处理操作(六)——直方图与模版匹配
直方图 cv2.calcHist(images,channels,mask,histSize,ranges) images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的…...
【LLM学习笔记】第四篇:模型压缩方法——量化、剪枝、蒸馏、分解
文章目录 1. 为什么要进行模型压缩2. 模型量化2.1 常见数据类型2.2 浮点数表示2.3 线性量化2.4 非线性量化2.5 挑战2.6 实际应用 3. 模型剪枝4. 模型蒸馏4.1 模型蒸馏的基本流程4.2 模型蒸馏的优势4.3 实际应用 5. 低秩分解(低秩近似)5.1 基本概念5.2 实…...
python3 自动更新的缓存类
这个类会在后台自动更新缓存数据,你只需要调用方法来获取数据即可。 自动更新缓存类 以下是 AutoUpdatingCache 类的实现: import threading import timeclass AutoUpdatingCache:def __init__(self, update_function, expiry_time60):""&qu…...
英语知识网站开发:Spring Boot框架应用
3系统分析 3.1可行性分析 通过对本英语知识应用网站实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本英语知识应用网站采用SSM框架,JAVA作为开发语…...
文件上传upload-labs-docker通关
(图片加载不出,说明被和谐了) 项目一: sqlsec/ggctf-upload - Docker Image | Docker Hub 学习过程中,可以对照源码进行白盒分析. 补充:环境搭建在Linux虚拟机上的同时,以另一台Windows虚拟机进行测试最…...
git(Linux)
1.git 三板斧 基本准备工作: 把远端仓库拉拉取到本地了 .git --> 本地仓库 git在提交的时候,只会提交变化的部分 就可以在当前目录下新增代码了 test.c 并没有被仓库管理起来 怎么添加? 1.1 git add test.c 也不算完全添加到仓库里面&…...
Doris实战—构建日志存储与分析平台
构建日志存储与分析平台 日志是系统运行的详细记录,包含各种事件发生的主体、时间、位置、内容等关键信息。出于运维可观测、网络安全监控及业务分析等多重需求,企业通常需要将分散的日志采集起来,进行集中存储、查询和分析,以进一步从日志数据里挖掘出有价值的内容。 针…...
【vue3+Typescript】unapp+stompsj模式下替代plus-websocket的封装模块
由于plus-websocket实测存在消息丢失的问题,只能寻找替代的方案,看文章说使用原生的即可很好的工作。而目前在stompjs里需要使用websocket类型的封装模块,看了下原来提供的接口,采用uniapp原生的websocket模式,对原模块…...
Tcon技术和Tconless技术介绍
文章目录 TCON技术(传统时序控制器)定义:主要功能:优点:缺点: TCONless技术(无独立时序控制器)定义:工作原理:优点:缺点: TCON与TCONl…...
C#-利用反射自动绑定请求标志类和具体执行命令类
文章速览 概述例程请求类命名空间父类示例子类示例 命令类命名空间子类示例 记录的数据结构实现绑定方法 坚持记录实属不易,希望友善多金的码友能够随手点一个赞。 共同创建氛围更加良好的开发者社区! 谢谢~ 概述 需求: 将指定的两种类型的…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...
【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...
