C++中的动态内存管理
1.C++中动态内存管理
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
1.1 new/delete操作内置类型
- c语言和c++的动态内存开辟对比
//c语言和c++的动态内存开辟对比
int main()
{//C语言的动态内存开辟int* p1 = (int*)malloc(sizeof(int);if (p1 == NULL){perror("malloc");}//c++动态的内存开辟int* p2 = new int;//不会初始化int* p3 = new int(10);//初始化,一个intint* p4 = new int[10];//开辟10个int型的空间int* p5 = new int[10]{ 1,2,3,4,5, };//开辟10个int型的空间并初始化为{1,2,3,4,5,0,0,0,0,0,0}free(p1);delete p2;delete p3;delete[] p3;delete[] p4;return 0;
}
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。
- malloc 和free ,new和delete,new [],delete[] 都要配对使用,如果交叉使用可能会有奇怪的问题。
1.2 new和delete操作自定义类型
- C语言中动态内存开辟和c++中动态内存开辟的对比,当然c++兼容c的动态内存开辟。
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};struct ListNode
{int _val;ListNode* _next;ListNode(int val):_val(val), _next(nullptr){}
};//ListNode BuyListNode(int x)
//{
// //...
//}int main()
{// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);free(p1);delete p2;// 内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int;
free(p3);
delete p4;A* p5 = (A*)malloc(sizeof(A)*10);A* p6 = new A[10];free(p5);delete[] p6;//与节点的对比,与c语言中ListNode* n1 = new ListNode(1);ListNode* n2 = new ListNode(2);ListNode* n2 = new ListNode(3);return 0;
}
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
2. operator new与operator delete函数
2.1 operator new与operator delete函数(重点)
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是
系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过
operator delete全局函数来释放空间.
- operator new是个函数名,与运算符重载没有关系。
- c++和c的内存泄漏都不会报错,都是自己管理的,要求你自己释放。java 有垃圾回收,在虚拟机实现,对象用完会自动释放,但是对性能有影响。
- new的底层是operator new ,operator new的底层是malloc,delete同理。
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{_CrtMemBlockHeader * pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg( pUserData, pHead->nBlockUse );__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
- c语言和C++开辟空间的对比,new和malloc ,free和delete之间的对比
class Stack
{
public:Stack(){cout << "Stack()" << endl;_a = new int[4];_top = 0;_capacity = 4;}~Stack(){cout << "~Stack()" << endl;delete[] _a;_top = _capacity = 0;}private:int* _a;int _top;int _capacity;
};class A
{};
int main()
{int* p1 = (int*)operator new(sizeof(int));//开辟失败会抛出异常int* p2 = (int*)malloc(sizeof(int));//开辟失败返回nullptrif (p2 == nullptr){perror("malloc");}//申请空间-》调用构造函数//封装malloc到operator new,开辟失败抛出异常,所以new的底层还是mlloc只不过封装了一些异常,保持面向对象的特性A* p3 = new A;//先调用析构函数-》再释放p5的空间//operator deletedelete p3;//operator new[] 调用了operator new,一样的A* p4 = new A[10];//调用10次构造函数//operator delete[] p4指向的空间。delete[] p4;//调用10次析构函数//这种不匹配不会报错,都是内置类型int* p5 = new int[10];free(p5);//这种不匹配不会报错,A没有资源释放A* p6 = new A;free(p6);//没有调用析构函数,但是类里面没有需要释放的内存,所以不会内存泄漏Stack st;//不用释放内存,因为这是自定义类型,生命周期结束会自动调用析构函数Stack* pst = new Stack;//delete pst;//先调用析构函数,再去释放pst指向的空间//free(pst);//只释放了pst指向的空间,没有调用析构函数释放类里面开辟的空间,而且c/c++都不会报错A* p7 = new A[10];//delete p7;//这个只会释放一个A的空间delete[] p7;//free(p7);//不会报错,会释放所有空间(这里可能有点问题,用到再看看)return 0;
}
- delete调用析构函数的流程 和 自定义类型直接结束自己调用析构
如果只delete一个A对象是从p9开始释放一个对象,但是如果有[],它就会减一个位置,然后知道要释放10个对象,仅限vs编译器是这样
3.new和delete的实现原理
3.1 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
3.2 自定义类型
- new的原理
调用operator new函数申请空间
在申请的空间上执行构造函数,完成对象的构造
- delete的原理
在空间上执行析构函数,完成对象中资源的清理工作
调用operator delete函数释放对象的空间
- **new T[N]**的原理
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
在申请的空间上执行N次构造函数
- delete[]的原理
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
4. 定位new表达式(placement-new) (了解)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
// 定位new/replacement new
int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参//对一块已有的空间初始化 --- 定位new//new(p1)A;p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);return 0;
}
class A
{
public:A(){}A(int a){}
};
int main()
{A* p1 = (A*)malloc(sizeof(A));if (p1 == nullptr){perror("malloc");}//对一块已有的空间初始化 --- 定位new//new(p1)A;new(p1)A(1);return 0;A* p2 = new A;p1->~A();free(p1);delete p2;
}
程序需要频繁使用内存,就提出使用内存池的机制。
向操作系统的堆申请空间比较慢,因为操作系统所有的都向这里申请,如果想要快点,就建立一个内存池,有位置就申请,无位置就继续返回操作系统申请
- 以下是关于C/C++内存分布的更多信息和示例:
- 多线程与内存分布:在多线程环境中,每个线程都有自己的栈区,用于存储线程执行过程中的现场信息。而堆区和全局变量区则被所有线程共享。因此,在多线程编程中,需要注意同步和互斥机制的使用,以避免因多个线程同时访问同一块内存而导致的数据竞争。
示例:
#include <iostream>
#include <thread>void thread_func() {std::cout << "Thread ID: " << std::this_thread::get_id() << '\n';
}int main() {std::thread t1(thread_func);std::thread t2(thread_func);t1.join();t2.join();return 0;
}
- 内存映射文件:内存映射文件是一种将磁盘文件映射到内存空间的技术。通过内存映射文件,程序可以直接访问文件内容,而无需通过传统的文件读写操作。这种技术可以提高程序的性能,简化文件操作。
示例:
#include <iostream>
#include <fstream>
#include <sys/mman.h>
#include <fcntl.h>int main() {const char* filename = "example.txt";int fd = open(filename, O_RDONLY);void* map_ptr = mmap(NULL, sizeof(int), PROT_READ, MAP_PRIVATE, fd, 0);int value = *(int*)map_ptr;std::cout << "Value from file: " << value << '\n';munmap(map_ptr, sizeof(int));close(fd);return 0;
}
以上示例和信息展示了C/C++内存分布的更多应用场景和技巧。如有更多问题,请随时提问。
相关文章:

C++中的动态内存管理
1.C中动态内存管理 C语言内存管理方式在C中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。 1.1 new/delete操作内置类型 c语言和c的动态内存…...
es6的核心语法
在学习低代码时,经常有粉丝会问,低代码需要什么基础,es6就是基础中的一项。我们本篇是做一个扫盲,可以让你对基础有一个概要性的了解,具体的每个知识点可以深入进行了解,再结合官方模板就会有一个不错的掌握…...

Unity | 射线检测及EventSystem总结
目录 一、知识概述 1.Input.mousePosition 2.Camera.ScreenToWorldPoint 3.Camera.ScreenPointToRay 4.Physics2D.Raycast 二、射线相关 1.3D(包括UI)、射线与ScreenPointToRay 2.3D(包括UI)、射线与ScreenToWorldPoint …...
职业经验 2024 年测试求职手册
原贴地址: 2024 年测试求职手册 TesterHome 经历年前年后差不多 2 个月左右时候的求职,是时候总结复盘一下了,本打算在自己有着落再复盘,但是一想那时候似乎价值就没现在去做显得有意义一些,这篇帖子更多的是让大家看下有没有心…...
Spring Boot与Redis深度整合:实战指南
Spring Boot 整合 Redis 相当简单,它利用了 Spring Data Redis 项目,使得我们可以在 Spring Boot 应用中轻松地操作 Redis。以下是如何整合 Redis 到 Spring Boot 应用的基本步骤: 1. 添加依赖 首先,在你的 pom.xml 文件中添加 …...

微服务(基础篇-006-Docker安装-CentOS7)
目录 05-初识Docker-Docker的安装_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p46&spm_id_frompageDriver&vd_source60a35a11f813c6dff0b76089e5e138cc 0.安装Docker 1.CentOS安装Docker 1.1.卸载(可选) 1.2.安装dock…...

前端-css-01
1.CSS 长度单位和颜色设置 1.1CSS 中的长度单位 px 像素 em 字体大小的倍数(字体默认是16px) % 百分比 1.2CSS 中的颜色设置方式 1.2.1使用颜色名表示颜色 red、orange、yellow、green、cyan、blue、purple、pink、deeppink、skyblue、greenyellow .…...
Java学习36-Java 多线程安全:懒汉式和饿汉式
JAVA种有两种保证线程安全的方式,分别叫懒汉式Lazy Initialization和饿汉式Eager Initialization,以下是他们的区别: 线程安全性: 懒汉式本身是非线程安全的,因为多个线程可能同时检查实例是否为null,并尝…...
sql常用之CASE WHEN THEN
sql常用之CASE WHEN THEN SQL中的 CASE 类似编程语言里的 if-then-else 语句,用做逻辑判断。可以用于SELECT语句中,也可以用在WHERE,GROUP BY 和 ORDER BY 子句;可以单独使用,也可以和聚合函数结合使用。 语法&#…...

【PduR路由】IPduM模块详细介绍
目录 1.IpduM功能简介 2.IpduM模块依赖的其他模块 2.1RTE (BSW Scheduler) 2.2PDU Router 2.3COM 3.IpduM功能详解 3.1 功能概述 3.2 I-PDU多路复用I-PDU Multiplexing 3.2.1 Definitions and Layout 3.2.2通用功能描述 General 3.2.3模块初始化 Initialization 3.…...

【MySQL】6.MySQL主从复制和读写分离
主从复制 主从复制与读写分离 通常数据库的读/写都在同一个数据库服务器中进行; 但这样在安全性、高可用性和高并发等各个方面无法满足生产环境的实际需求; 因此,通过主从复制的方式同步数据,再通过读写分离提升数据库的并发负载…...

Lucene及概念介绍
Lucene及概念介绍 基础概念倒排索引索引合并分析查询语句的构成 基础概念 Document:我们一次查询或更新的载体,对比于实体类 Field:字段,是key-value格式的数据,对比实体类的字段 Item:一个单词࿰…...

密码算法概论
基本概念 什么是密码学? 简单来说,密码学就是研究编制密码和破译密码的技术科学 例题: 密码学的三个阶段 古代到1949年:具有艺术性的科学1949到1975年:IBM制定了加密标准DES1976至今:1976年开创了公钥密…...

实时数仓之实时数仓架构(Hudi)
目前比较流行的实时数仓架构有两类,其中一类是以FlinkDoris为核心的实时数仓架构方案;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍,这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…...
2022-04-15_for循环等_作业
for循环 编写程序数一下 1到 100 的所有整数中出现多少个数字9计算1/1-1/21/3-1/41/5 …… 1/99 - 1/100 的值,打印出结果求10 个整数中最大值在屏幕上输出9*9乘法口诀表二分查找 编写程序数一下 1到 100 的所有整数中出现多少个数字9 #include <stdio.h>in…...

脑机辅助推导算法
目录 一,背景 二,华容道中道 1,问题 2,告诉脑机如何编码一个正方形格子 3,让脑机汇总信息 4,观察图,得到启发式算法 5,根据启发式算法求出具体解 6,可视化 一&am…...

【原创教程】三菱FX PLC控制FR-E740变频器
变频器的使用 1. 使用三菱FX PLC 控制变频器时,接线图请按下图所示接线。 各个端子的说明如下: R、S、T:变频器电源,E740变频器电源位3相380V。 STF:正转启动, STF信号ON时为正转、OFF时为停止指令。 STR :反转启动,STR信号ON时为反转、OFF时为停止指令。 RH、RM、RL…...

重读Java设计模式: 深入探讨建造者模式,构建复杂对象的优雅解决方案
引言 在软件开发中,有时需要构建具有复杂结构的对象,如果直接使用构造函数或者 setter 方法逐个设置对象的属性,会导致代码变得冗长、难以维护,并且容易出错。为了解决这个问题,我们可以使用建造者模式。 一、建造者…...

C语言数据结构易错知识点(6)(快速排序、归并排序、计数排序)
快速排序属于交换排序,交换排序还有冒泡排序,这个太简单了,这里就不再讲解。 归并排序和快速排序都是采用分治法实现的排序,理解它们对分支思想的感悟会更深。 计数排序属于非比较排序,在数据集中的情况下可以考虑使…...
使用 React Router v6.22 进行导航
使用 React Router v6.22 进行导航 React Router v6.22 是 React 应用程序中最常用的路由库之一,提供了强大的导航功能。本文将介绍如何在 React 应用程序中使用 React Router v6.22 进行导航。 安装 React Router 首先,我们需要安装 React Router v6…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...