C++的内存管理

news2024/9/23 1:20:52

目录

1. C/C++内存分布

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

 3. C++内存管理方式

3.1 new/delete操作内置类型

4. operator new与operator delete函数

 4.1 连续开辟空间(尽力了解)

 5. new和delete的实现原理

 5.1 内置类型

5.2 自定义类型

6. 深入理解

6.1malloc/free和new/delete的区别

7.内存泄漏

7.1 什么是内存泄漏,内存泄漏的危害

 7.2 内存泄漏分类(了解)

7.3 如何检测内存泄漏(了解)

 7.4 避免内存泄漏


1. 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.代码段(常量区)  

globalVar在哪里?__c__   staticGlobalVar在哪里?__c__  

staticVar在哪里?___c_   localVar在哪里?__a__  

num1 在哪里?__a__    

char2在哪里?__a__   *char2在哪里?_a__  

pChar3在哪里?__a__     *pChar3在哪里?_d___  

ptr1在哪里?__a__        *ptr1在哪里?_b___

1. char2代表在常量区有一个字符串"abcd/0" 去初始化,而会在栈上开辟一个数组把这个字符串拷贝过去,所以char2和num1是类似的,只不过num1是自己开辟的空间而char2的空间由字符串决定, *char2代表取首元素在栈上因为在栈上有自己的字符串在字符数组里
2. const修饰的变量不代表一定在常量区,他只是常变量,pchar3只是栈上的一个指针变量,这个指针存了一个地址指向常量区abcd这个字符串,所以*pchar3找到的是常量区的a
3. 同样ptr1也是栈上的指针,他指向堆上的一块空间。所以*ptr1在堆上

字符串常量存储在常量区 

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

主要分为:malloc/calloc/realloc/free

calloc比malloc多了一步就是初始化为0

realloc为扩容(分为原地和异地扩容),如果空间够的话就原地扩容那么返回的指针跟要扩容的指针是一样的。异地扩容的话会把原来的拷贝后释放原来的空间。
如果内存块足够大realloc会尝试在原地扩大内存块;如果无法在原地扩大,则会分配新的内存块,将原内容复制到新的内存块中,并释放原内存块。

例如

int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
free(p3 );

 3. C++内存管理方式

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

3.1 new/delete操作内置类型

struct listnode {
	struct listnode* _pre;
	struct listnode* _next;
	int _val;
	listnode(int val)//所以这里写构造函数不然new不出来
		:_pre(nullptr)
		,_next(nullptr)
		,_val(val)
	{}

};
struct listnode* creatnode(int val) {
	struct listnode* ret = (struct listnode*)malloc(sizeof(struct listnode));
	if (ret == NULL) {
		perror("malloc fail");
		return NULL;
	}
	ret->_next = NULL;
	ret->_pre = NULL;
	ret->_val =val;
}
int main() {
		// 动态申请一个int类型的空间
		int* ptr4 = new int;//在堆上要一个int对象
		// 动态申请一个int类型的空间并初始化为10
		int* ptr5 = new int(10);//在堆上要一个int对象初始化为10
		// 动态申请10个int类型的空间
		int* ptr6 = new int[3];//要3个int对象
		int* ptr7 = new int[10] {1, 2, 3, 4, 5};//其余未初始化的为0
		delete ptr4;
		delete ptr5;
		delete[] ptr6;//要匹配,newp[]就要delete[]
		//自定义类型 开空间自动调用构造函数
		listnode* p1 = new listnode(1);
		listnode* p2 = new listnode(2);
		listnode* p3 = new listnode(3);
		return 0;
}

可以看出用法上变简介了,同时也可以初始化了,同时new失败了以后不需要手动检查

 

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与 free不会。 

相对于c语言的好处:

1. 用法上进行调整,更简洁好用

2. 对于自定义类型更好的初始化,new支持开空间+构造函数初始化

4. operator new与operator delete函数

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

operator new是对malloc的封装。operator delete是对free的封装

int* p1 = (int*)operator new(10 * 4);//不经常用
int* p2 = (int*)new(10 * 4);//不可以这样

可以看出跟malloc一样
可以看出new底层调用operator new

抛异常是在operator new出现的,operator new[]就是封装operator new

关于new:

1. malloc在调用失败后会返回空,但是C++是一种面向对象的,所以需要进行异常抛出,(malloc失败后是需要我们自己检查,或者我们提前写出一个失败后的检查机制,但是operator new 与new 是对malloc的封装,它封装的内部具有抛异常的函数)
2. 但是operator new 和 malloc的用法一致虽然多了一个检查内部异常,但是大部分程序员是不会使用的,程序员使用最多的还是new 和 delete 
3. 而且,new 的内部其实是 operator new 和调用构造函数 ,而operator new 封装了malloc 和检查异常功能
4. new 的本质是一个操作符,它的底层是malloc函数,在编译器碰见new时就会自动转化为对应的代码指令

 operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。

  使用delete是先析构后调用operator delete

 4.1 连续开辟空间(尽力了解)

class A {
public:
	A(int a=0)
		:_a(a)
	{
		cout << "A():" <<this<< endl;
	}
	~A() {
		cout << "~A()" << this << endl;
	}
private:
	int _a;
};
int main() {
	A* p1 = new A[10];
}
左边开辟40字节,右边开辟44字节

自定义类型使用new连续开辟空间时, 所产生的字节数会比我们需要开辟的字节数多,这个原因其实是delete的书写格式造成的。 

因为delete 释放数组空间的时候,是不会在[]内部加上数字的,所以需要有一个东西让delete知道要释放多少字节,而new []会多次一个字节的空间,这个空间就算存放一个数字,给予delete进行识别要释放多少字节

当然,多开辟空间的问题只是针对于自定义类型,而内置类型并不需要多开辟空间,但是自定义类型也需要看情况:如果自定义类型没有写析构函数,而是编译器自动生成的析构函数则也不需要多开辟空间 

一定要匹配使用

同时,就算有析构函数但按照不匹配的写法,那么释放的位置就会出错,他少释放了最开始访问数字识别的位置 ,简单来说,有析构函数就会多开辟空间,但是不匹配的写法会少释放那个开辟的空间 

知道有这么个东西,有一定的了解即可,就像不需要了解具体的历史只需具备常识 

 5. new和delete的实现原理

 5.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是: new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申 请空间失败时会抛异常,malloc会返回NULL。

5.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来释放空间

6. 深入理解

6.1malloc/free和new/delete的区别

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在释放空间前会调用析构函数完成 空间中资源的清理

 总结:new更方便

7.内存泄漏

7.1 什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

总结说:
内存泄漏:一块已经不再使用的空间,没有释放,不让别人用(占着茅坑不拉屎) 

void MemoryLeaks()
{
   // 1.内存申请了忘记释放
  int* p1 = (int*)malloc(sizeof(int));
  int* p2 = new int;
  
  // 2.异常安全问题
  int* p3 = new int[10];
  
  Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
  
  delete[] p3;
}

 7.2 内存泄漏分类(了解)

C/C++程序中一般我们关心两种方面的内存泄漏:

堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一 块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

系统资源泄漏指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

7.3 如何检测内存泄漏(了解)

在vs下,可以使用windows操作系统提供的_CrtDumpMemoryLeaks() 函数进行简单检测,该 函数只报出了大概泄漏了多少个字节,没有其他更准确的位置信息。

int main()
{
    int* p = new int[10];
    // 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
    _CrtDumpMemoryLeaks();
    return 0;
}
 
// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
Detected memory leaks!
Dumping objects ->
{79} normal block at 0x00EC5FB8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

 因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜防,简单的可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内存泄漏检测工具处理的。

 7.4 避免内存泄漏(了解)

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记得匹配的去释放。ps:这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
2. 采用RAII思想或者智能指针来管理资源。
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
4. 出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。

 由于知识有限可能部分知识没讲详细,请理解!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1536572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索多视角驱动的层次内容感知网络用于指静脉识别

文章目录 探索多视角驱动的层次内容感知网络用于指静脉识别总结摘要介绍相关工作多视角方法长短时记忆基于视角的目标表达 方法全局主干网络局部感知模块损失函数 实验和分析数据库实验设置和训练策略消融实验视角一致性的效果 参考文献 论文: Exploiting Multiperspective Dr…

小米相册提取表格选项消失解决方法

小米相册这次的提取表格选项消失 故障原因&#xff1a; 因为部分用户将小爱视觉&#xff08;原名扫一扫&#xff09;这个应用给卸载了导致 解决方法 应用商店下载 小爱视觉 安装后授权即可使用 注意&#xff1a;系统最好为最新的 Xiaomi HyperOS系统

抖音小店保证金是多少?怎么缴纳?最全的解答来了!

大家好&#xff0c;我是电商糖果 关于抖音小店的保证金&#xff0c;很多人并不太了解。 再加上保证金每年都会有变动&#xff0c;造成很多想开店的商家完全不懂&#xff0c;应该准备多少钱&#xff0c;以及如何缴纳&#xff1f; 这里糖果就给大家总结一下&#xff0c;想开店…

基于SSM+Jsp+Mysql的高校二手交易平台

基于SSMJspMysql的高校二手交易平台 基于SSMJspMysql的高校二手交易平台的设计与实现 开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff0…

JetBrains全家桶激活,分享 GoLand 2024 激活的方案

大家好&#xff0c;欢迎来到金榜探云手&#xff01; GoLand 公司简介 JetBrains 是一家专注于开发工具的软件公司&#xff0c;总部位于捷克。他们以提供强大的集成开发环境&#xff08;IDE&#xff09;而闻名&#xff0c;如 IntelliJ IDEA、PyCharm、和 GoLand等。这些工具被…

AI智能分析网关V4在非煤矿山安全生产视频智能监管场景中的应用

近年来&#xff0c;全国非煤矿山&#xff08;&#xff08;含金属非金属矿山、尾矿库&#xff0c;以及矿泉水等其他矿山&#xff09;安全生产工作取得明显成效&#xff0c;但安全基础仍然薄弱&#xff0c;事故总量仍然较大&#xff0c;重特大事故尚未得到根本遏制&#xff0c;安…

shell编程-jq命令详解

文章目录 前言一、jq简介1. 简介2. 语法3. 命令选项 二、用于处理json数据1. 过滤1.1 标识运算符1.2 基本过滤1.3 获取对象属性1.3 迭代数组元素1.4 获取数组元素1.5 使用运算符 2. 类型和值2.1 数组构造2.2 对象构造2.3 递归下降 3. 内置运算符和函数3.1 算术运算符3.2 函数3.…

外包干了20天,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;21年通过校招进入杭州某软件公司&#xff0c;干了接近2年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了2年的功能测试…

20240316-2-协同过滤(collaborative filtering)

协同过滤(collaborative filtering) 直观解释 协同过滤是推荐算法中最常用的算法之一&#xff0c;它根据user与item的交互&#xff0c;发现item之间的相关性&#xff0c;或者发现user之间的相关性&#xff0c;进行推荐。比如你有位朋友看电影的爱好跟你类似&#xff0c;然后最…

PublicCMS 后台模块 站点执行脚本RCE漏洞

PublicCMS是采用2023年主流技术开发的开源JAVACMS系统。由天津黑核科技有限公司开发&#xff0c;架构科学&#xff0c;轻松支撑上千万数据、千万PV&#xff1b;支持可视化编辑&#xff0c;多维扩展&#xff0c;全文搜索&#xff0c;全站静态化&#xff0c;SSI&#xff0c;动态页…

基于Spring Boot的拍卖管理系统设计与实现

摘 要 随着社会的发展&#xff0c;系统的管理形势越来越严峻。越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得信息&#xff0c;因此&#xff0c;设计一种安全高效的拍卖管理系统极为重要。 为设计一个…

【Linux】Linux工具学习之gcc/g++

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 接上文&#xff0c;我们已经学习了 Linux 中的编辑器 vim 的相关使用方法&#xff0c;现在…

就业班 第二阶段 2401--3.19 day2 DDL DML DQL 多表查询

在mysql库里的语句 \G 竖着排列 ; \g 横着排列 数据库用户组成 双单引号单都行 -- sql的注释 创建mysql用户&#xff1a;&#xff08;兼容5.7 8.0 &#xff09; create user root% identified by Qwer123..; grant all on *.* to root%; flush privileges; mysql 5.7 grant …

使用easyYapi生成文档

easyYapi生成文档 背景1.安装配置1.1 介绍1.2 安装1.3 配置1.3.1 Export Postman1.3.2 Export Yapi1.3.3 Export Markdown1.3.4 Export Api1.3.6 常见问题补充 2. java注释规范2.1 接口注释规范2.2 出入参注释规范 3. 特定化支持3.1 必填校验3.2 忽略导出3.3 返回不一致3.4 设置…

【单调队列】第十四届蓝桥杯省赛C++ C组 Java C组/研究生组 Python A组《子矩阵》(C++)

【题目描述】 给定一个 nm &#xff08;n 行 m 列&#xff09;的矩阵。 设一个矩阵的价值为其所有数中的最大值和最小值的乘积。 求给定矩阵的所有大小为 ab &#xff08;a 行 b 列&#xff09;的子矩阵的价值的和。 答案可能很大&#xff0c;你只需要输出答案对 998244353…

羽毛球移动步法训练

文章目录 简介训练量01 小颠步02 左右并步03 交叉步04 前后并步05 高抬腿06 高抬腿转髋07 左右移动跳08 并步跳09 前后小密步10 弓箭步跳11 开合跳参考文献 简介 本文参考&#xff1a;陈金羽毛球移动专项步法训练&#xff08;BV1cS4y1V7xA&#xff09;和颜叨叨的移动专项步法跟…

数据表练习

思维导图 面试题答问1、IO多路复用的引入目的和原理 目的&#xff1a;在有操作系统时&#xff0c;可以用多线程和进程完成任务并发执行&#xff0c;没有操作系统的情况下可以使用IO多路复用技术来进行任务并发。 原理&#xff1a;将多个阻塞任务的文件描述符统一放到一个检查容…

Qt 容器类控件

Group Box 使用 QGroupBox 实现一个带有标题的分组框可以把其他的控件放到里面作为一组&#xff0c;这样看起来能更好看一点. 核心属性 属性说明title分组框的标题alignment分组框内部内容的对齐方式flat是否是 “扁平” 模式checkable是否可选择. 设为 true&#xff0c;则在…

Golang基础知识(笔记迁移)

golang 变量作用域 局部作用域&#xff1a;代码块、函数内的全局作用域&#xff1a;顶层作用域&#xff0c;代码块外的就是全局&#xff0c;如果变量名大写&#xff0c;则改变量整个程序都可以使用。 类型断言 golang的类型断言在变量后加上.(type)&#xff0c;如果类型断言…

【Android】系统启动流程分析 —— init 进程启动过程

本文基于 Android 14.0.0_r2 的系统启动流程分析。 一、概述 init 进程属于一个守护进程&#xff0c;准确的说&#xff0c;它是 Linux 系统中用户控制的第一个进程&#xff0c;它的进程号为 1&#xff0c;它的生命周期贯穿整个 Linux 内核运行的始终。Android 中所有其它的进程…