C++——内存管理

ops/2024/9/20 1:57:09/ 标签: c++, 算法, 内存管理

目录

引言

C/C++的内存分布

C语言中动态内存管理方式

C++内存管理方式

1.new/delete操作内置类型

2.new与delete操作自定义类型

operator new与operator delete函数

new与delete的实现

1.内置类型

2.自定义类型

定位new表达式

malloc/free和new/delete的区别

结束语


引言

在简单的学习完类与对象之后,我们接下来学习C++的内存管理

求点赞收藏评论关注!!!

C/C++的内存分布

我们先来看一段代码以及问题:

int globalVar = 1;
static int staticGlobalVar = 1;void Test()
{static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}

选择题:

问题:以上数据存放在什么位置?

选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

1.globalVar 在哪里?                        C
2.staticGlobalVar 在哪里?              C
3. staticVar 在哪里?                        C
4. localVar 在哪里?                         A
5.num1 在哪里?                             A  
6.char2 在哪里?                             A
7.*char2 在哪里?                            A
8. pChar3在哪里?                          A
9.*pChar3 在哪里?                        D
10.ptr1 在哪里?                             A
11.*ptr1 在哪里?                            B

解析:

1.globalVar 是全局变量,全局变量放在静态区。 C

2.staticGlobalVar 也是全局静态变量,放在静态区。与globalVar 的区别是: 普通全局变量作用于整个代码,可被其他文件访问或修改。staticGlobalVar 被 static 修饰,被 static 修饰的静态全局变量只作用于当前文件,其他文件不可见。 C

3.staticVar 是局部静态变量,局部静态变量虽然访问作用域在函数中,但它与全局变量一样,存放在静态区。 C

4.localVar 是局部变量,局部变量放在栈。被static修饰的局部变量的生命周期只会在程序结束后结束,而普通的局部变量的生命周期出了当前作用域就会结束。 A

5.num1 是数组名,是一个局部变量,放在栈区。 A

6.char2 也是一个局部变量,放在栈区,常量字符串"abcd"放在代码段(常量区),数组开辟的空间放在栈区。 A

7.*char2 这里表示的是数组首元素的地址,解引用即表示第一个元素 ‘a’。虽然我们知道常量字符串是存储在常量区,但是,数组中存的 “abcd” 是从常量区中 拷贝 过来存储在数组中,依然是在栈上。 A

8.pChar3 是指针变量,是局部变量,是在栈上开辟空间存储 “abcd” 首字符的地址的变量。 A

9.*pChar3 指向 “abcd” 首元素的地址 ‘a’。“abcd” 是只读常量,放在常量区。 D

10.ptr1 与 pChar3 同理,都是存储在栈上。 A

11.ptr1 指向动态开辟的空间,而 *ptr1 则是问动态开辟出的空间存储在哪,存储在堆上。 B

1.栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。

2.内存映射段是高效的I/0映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

3.堆用于程序运行时动态内存分配,堆是可以上增长的。

4.数据段--存储全局数据和静态数据,

5.代码段--可执行的代码/只读常量。

C语言中动态内存管理方式

C语言中有关动态内存管理的内容在我的博客:C语言——动态内存管理 中有比较详细的介绍。欢迎各位大佬能阅读一下。

C++内存管理方式

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过 new delete 操作符进行动态内存管理

1.new/delete操作内置类型

new 运算符用于动态分配内存,并返回指向该内存的指针。它可以与单个对象或对象数组一起使用。

delete 运算符用于释放之前由 new 运算符分配的内存。它必须与 new 运算符成对使用,以避免内存泄漏。

使用示例如下:

int main()
{// 动态分配一个整型变量的内存,并将地址赋给指针p1 int* p1 = new int;// 动态分配一个包含10个整型变量的数组的内存,// 并将数组首地址赋给指针p2int* p2 = new int[10];// 注意:这里的数组也是未初始化的,数组中的所有元素都是未定义的。// 动态分配一个整型变量的内存,并初始化为10,然后将地址赋给指针p3int* p3 = new int(10);// 这里,p3指向的整型变量被明确初始化为10。//动态申请5个int类型空间,并将前面2个初始化为1,后面默认初始化为0int* p4 = new int[5] {1, 1};//与C语言free功能类似,释放空间防止内存泄漏delete p1;delete[] p2;delete p3;delete[] p4;return 0;
}

我们可以通过监视窗口来观察一下:

2.new与delete操作自定义类型

使用new和delete来操作内置类型(如int、float、char等)时,它们在底层的行为上与C语言中的malloc和free没有太大的本质区别。这主要体现在它们都是用来在堆上分配和释放内存的。然而,它们之间还是存在一些关键的区别和考虑因素,尤其是在处理自定义类型时。

来看看这段代码:

class A
{
public:A(int a = 0):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}void Print(){cout << "_a = " << _a << endl;}
private:int _a = 0;
};int main()
{A* p1 = new A;A* p2 = new A(10);p1->Print();p2->Print();delete p1;delete p2;return 0;
}

运行结果为:

我们可以得知:new在创建自定义类型时会自动调用其构造函数,delete在释放其空间时会自动调用其析构函数。

operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和 operator delete 是系统提供的全局函数,new 在底层调用 operator new 全局函数来申请空间,delete 在底层通过 operator delete全局函数来释放空间。

下面是operator new与operator delete函数的源代码:

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* 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;
}

在C++中,operator new 和 operator delete 是全局函数,它们分别用于内存的申请和释放。这些函数在内部通常会使用C语言风格的内存管理函数(如malloc和free)来执行实际的内存分配和释放操作,但它们提供了额外的功能,特别是异常处理和类型安全。

operator new可以配置为在内存分配失败时抛出一个std::bad_alloc异常,而不是简单地返回nullptr。这允许C++程序以一种更加面向对象和异常安全的方式来处理内存分配失败的情况。

operator delete 最终是通过free来释放空间的。

new与delete的实现

接下来我们来探讨一下new与delete的实现,来看一下这段代码:

class A
{
public:A(){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _x = 0;
};void Test()
{int* ptr1 = new int;	//内置类型A* ptr2 = new A;		//自定义类型delete ptr1;delete ptr2;
}

我们通过反汇编语言来观察一下:

1.内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

2.自定义类型

new的原理:
(1)调用operator new函数申请空间
(2)在申请的空间上执行构造函数,完成对象的构造

delete的原理:
(1)在空间上执行析构函数,完成对象中资源的清理工作
(2)调用operator delete函数释放对象的空间

new T[N]的原理:
(1)调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
(2)在申请的空间上执行N次构造函数

delete[]的原理:
(1)在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
(2)调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

定位new表达式

定位new表达式(placement new)是一种特殊的C++语法,允许开发者在已分配但未经初始化的内存区域上构造对象。

定位new的基本使用格式如下:

new (place_address) type;

或者,如果需要传递初始化列表给构造函数,可以这样做:

new (place_address) type(initializer-list);

place_address 是一个指向足够大且已分配(但可能未初始化)的内存块的指针,用于存储新构造的对象。

type 是要构造的对象的类型。

initializer-list 是一个可选的初始化列表,用于传递给对象的构造函数。

下面是个简单的使用示例:

class A 
{
public:A(int a = 0) : _a(a)  // 使用初始化列表显式初始化成员变量_a {cout << "A(int a = 0):" << _a << endl;}// 析构函数~A(){cout << "~A()\n";}
private:int _a;
};int main() 
{// 分配内存    void* memory = operator new(sizeof(A));// 使用定位new构造对象:在已分配的内存上构造A类的对象A* a = new (memory) A;// 显式调用析构函数    a->~A();// 释放内存    operator delete(memory);return 0;
}

输出结果为:

malloc/free和new/delete的区别

(1)malloc和free是函数,new和delete是操作符。
(2)malloc申请的空间不会初始化,new可以初始化。
(3)malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可。
(4)malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
(5)malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。
(6)申请自定义类型对象时,malloc / free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

结束语

写的有点墨迹了。。。

求点赞收藏评论关注!!!

感谢各位大佬支持!!!


http://www.ppmy.cn/ops/110660.html

相关文章

从简单分析到智能问数,Smartbi AIChat让数据回归业务

大数据产业创新服务媒体 ——聚焦数据 改变商业 在某科技公司&#xff0c;资深数据分析师李晨&#xff08;化名&#xff09;正忙于分析新产品的市场表现。面对传统自助式BI工具&#xff0c;李晨在功能界面中手动设置各种查询条件&#xff0c;进行了一番复杂的拖拉拽操作&#…

iPhone 16分辨率,屏幕尺寸,PPI 详细数据对比 iPhone 16 Plus、iPhone 16 Pro、iPhone 16 Pro Max

史上最全iPhone 机型分辨率&#xff0c;屏幕尺寸&#xff0c;PPI详细数据&#xff01;已更新到iPhone 16系列&#xff01; 点击放大查看高清图 &#xff01;

卷积神经网络(一)

目录 一.卷积神经网络的组成 二.卷积层 目的&#xff1a; 参数&#xff1a; 计算公式 卷积运算过程 三.padding-零填充 1.Valid and Same卷积 2.奇数维度的过滤器 四.stride步长 五.多通道卷积 1.多卷积核(多个Filter) 六.卷积总结 七.池化层(Pooling) 八.全连接层…

nginx部署时的路径配置问题

背景 一直觉得程序员敲代码就行了&#xff0c;结果前端一打包部署就给我打回原形了。每回部署都失败&#xff0c;然后我都形成惯性了&#xff0c;一到nginx部署我就摇人&#xff0c;我都不好意思了。 这一次的问题是原前端代码的基础路径为‘/’&#xff0c;现在要改成‘/abc’…

大数据-133 - ClickHouse 基础概述 全面了解

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

ubuntu内核升级后的问题修复

文章目录 需求当前环境禁止内核更新安装内核修复/usr/include/dlocate 测试 需求 升级后的常见问题 驱动程序不兼容: 新内核版本可能导致某些硬件驱动程序不再兼容&#xff0c;尤其是专有驱动程序或第三方驱动程序。启动问题:内核更新可能导致启动问题&#xff0c;例如无法启动…

第四章 类和对象 实践与练习(1)

综合练习 1 简易计算器 使用静态方法模拟一个只能进行两个数加减乘除的简易计算器。 static double a,b;public static void main(String[] args) {简易计算器01 sum new 简易计算器01();//创建一个对象System.out.println("4.4加上7.11的结果&#xff1a;"sum.add…

[数据集][目标检测]车油口挡板开关闭合检测数据集VOC+YOLO格式138张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;138 标注数量(xml文件个数)&#xff1a;138 标注数量(txt文件个数)&#xff1a;138 标注类别…

ModbusTCP/RTU转Ethernet/IP(CIP)-Modbus设备与罗克韦尔AB的PLC之间通讯

IGT-DSER智能网关模块支持西门子、三菱、欧姆龙、罗克韦尔AB等各种品牌的PLC之间通讯&#xff0c;同时也支持PLC与Modbus协议的工业机器人、智能仪表、变频器等设备通讯。网关有多个网口、串口&#xff0c;也可选择WIFI无线通讯。无需PLC内编程开发&#xff0c;只要在IGT-DSER智…

shader 案例学习笔记之将坐标系分成4个象限

代码&#xff1a; _st * 2.0;float index 0.0; index step(1., mod(_st.x,2.0)); index step(1., mod(_st.y,2.0))*2.0; 示意图&#xff1a; 计算左下角 计算右下角 计算左上角 计算右上角 最后结果示意&#xff1a; 坐标系被分成了4个单元格&#xff0c;每个单元格都有…

Kafka高吞吐量的原因

文章目录 生产者&#xff08;写入数据&#xff09;顺序写入Memory Mapped Files 消费者&#xff08;读取数据&#xff09;Kafka是如何巧妙设计的? 总结 众所周知kafka的吞吐量比一般的消息队列要高&#xff0c;号称the fastest&#xff0c;那他是如何做到的&#xff0c;让我们…

产品探秘|开物——面向AI原生和云原生网络研究的首选科研平台

在当今高速发展的信息技术领域&#xff0c;特别是对于那些致力于前沿科技探索与实践的高校而言&#xff0c;拥有一款能够支持复杂网络业务研究与开发的平台至关重要。开物™数据网络开发平台&#xff08;Data Network Development Platform&#xff0c;简称DNDP&#xff09;&am…

[WEBPWN]BaseCTF week1 题解(新手友好教程版)

WEB A Dark Room 这道题的考点是查看网页源代码 网页源代码这里看到的是网页的html css js在用户浏览器上执行的代码 有时候很多铭感信息&#xff0c;或者关键信息。 查看网页源代码的几种方式 1 右键点击查看网页源代码 2 F12 3 Ctrl U 快捷键 HTTP是什么 HTTP&#x…

ip属地河北切换北京

我们知道&#xff0c;每当电脑或手机连接网络时&#xff0c;都会分配到一个网络IP地址&#xff0c;这个IP地址通常与设备所在的地区网络相关联。然而&#xff0c;出于业务或个人需求&#xff0c;有时我们需要将本机的IP地址切换到其他城市。例如要将IP属地河北切换北京&#xf…

主流日志框架Logback与Log4j2

一、Logback 1、介绍 Logback是由log4j创始人设计的又一个开源日志组件。 Logback当前分成三个模块&#xff1a;logback-core&#xff0c;logback- classic和logback-access logback-core是其它两个模块的基础模块&#xff0c;类似与springframework logback-classic是log…

项目——负载均衡OJ

项目要实现的一个整体的功能&#xff1a; 编写一个在线OJ网络服务器,只实现类似 leetcode 的题目列表在线编程功能 项目宏观结构: Oj服务器在收到提交的代码时&#xff0c;把代码负载均衡的选择发送给其他几个编译与运行服务器去编译运行代码&#xff0c;判断代码的编译运行结…

python打包工具Nuitka使用介绍

首先说一下&#xff0c;为什么我选择Nuitka而不是pyinstaller&#xff1f; Nuitka相对pyinstaller有以下优点&#xff1a; 1. Nuitka能够对 Python 代码进行深度分析&#xff0c;并在此基础上生成优化后的 C 语言代码会针对生成的 C 代码进行更多的优化&#xff0c;这意味着生…

java面试题-Sql 语句的执行顺序

远离八股文&#xff0c;面试大白话&#xff0c;通俗且易懂 看完后试着用自己的话复述出来。有问题请指出&#xff0c;有需要帮助理解的或者遇到的真实面试题不知道怎么总结的也请评论中写出来&#xff0c;大家一起解决。 java面试题汇总-目录-持续更新中 Sql 语句的执行顺序 fr…

Ubuntu22.04安装nginx

1.安装nginx 首先&#xff0c;更新你的包索引&#xff1a; sudo apt update 安装必要的软件包以允许apt通过HTTPS使用仓库&#xff1a; sudo apt install ca-certificates curl gnupg lsb-release 添加Nginx官方的GPG密钥&#xff1a; curl -fsSL https://nginx.org/keys/ng…

简单计算机网络概念

1.浏览器过程 输入url&#xff0c;解析url 1.协议http、https的区别&#xff1b;HTTPS就是在HTTP与TCP之间增加了SSL/TSL安全传输层 2.格式&#xff1a;协议//主机:端口/路径&#xff1b; 3.HTTP版本&#xff1a;1.0和1.1 4.HTTP/1.1&#xff1a;1. 持久连接&#xff1a;为了…