【C++进阶之路】内存管理

news2025/1/18 14:04:12

文章目录

  • 一.内存管理
    • 1. 内存布局
    • 2. C++的内存管理
      • ①内置类型
      • ② 自定义类型
    • 3. operate new 与 operate delete
      • ① 解读operate new源代码
      • ② 解读operate delete源代码
    • 4. new和delete的基本原理
      • ①new对类对象
      • ②delete对类对象
    • 拓展—— 深入理解delete[]和new[]
    • 对比 C和C++内存管理的用法
    • 5. 定位new
      • 基本用法
    • 6. 内存泄漏

一.内存管理

1. 内存布局

在这里插入图片描述

  • 说明:内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
  • 说明:调用函数的传参是在栈上开辟空间的,且从右向左进行传参。函数的代码的执行也是在栈上的,不过是调用代码区的代码,在栈上执行而已。
  • 更多关于malloc/realloc/calloc/free以及内存空间的证明的信息:内存管理。

2. C++的内存管理

  • 区别于malloc等申请空间的函数,C++引出了new操作符
  • 区别于free释放空间的函数,C++引出了delete操作符
  • 说明: new和delete一定要配套使用

①内置类型

#include<iostream>
using namespace ::std;
int main()
{
	int* p = new int(3);
	delete p;
	return 0;
}

图解:
在这里插入图片描述

② 自定义类型

  • 数组
#include<iostream>
using namespace ::std;
int main()
{
	int* p = new int[6]{0};
	delete[] p;
	return 0;
}

在这里插入图片描述

#include<iostream>
using namespace ::std;
class Date
{
public:
	Date(int year)
	{
		cout << "Date" << endl;
		_year = year;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
};
int main()
{
	Date* p = new Date(1949);
	delete p;
	Date* p1 = new Date[4]{1949,2019,2022,2023};
	delete[] p1;
	Date* p2 = new Date[4]{ Date(1949),Date(2019),Date(2022),Date(2023) };
	delete[] p2;
	return 0;
}

程序运行结果:
在这里插入图片描述
图解:
在这里插入图片描述

  • 说明:对类来说,new一个对象会调用此对象的构造函数进行初始化,然而malloc一个对象仅仅是对一个对象开辟了空间,并没有调用构造函数

3. operate new 与 operate delete

  • 补充:异常,简单地说就是代码执行到某处中断了,无法继续执行,这时的情况就叫异常。
    平常我们在遇到对空指针解引用的情况时,就是出现异常了。
    图:
    在这里插入图片描述

① 解读operate new源代码

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	void* p;
	while ((p = malloc(size)) == 0)
		if (_callnewh(size) == 0)
		{
			// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
	return (p);
}

图解:
在这里插入图片描述

  • 因此:operate new 是一个函数,调用了malloc,并且如果开辟失败会出现异常
  • 为什么要这样做呢?因为面向对象语言不喜欢用返回值来处理问题,而更喜欢使用抛异常,来解决问题,通过异常我们可以判断出现了什么情况的错误。

② 解读operate delete源代码

void operator delete(void* pUserData)
{
	_CrtMemBlockHeader* pHead;
	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
	if (pUserData == NULL)
		return;
		
	_mlock(_HEAP_LOCK);
	__TRY
	pHead = pHdr(pUserData);
	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
	_free_dbg(pUserData, pHead->nBlockUse);
	__FINALLY
		_munlock(_HEAP_LOCK); 
	__END_TRY_FINALLY
		return;
}

图解:
在这里插入图片描述

  • 因此:operate delete释放空间的操作跟free本质上是相同的

那了解了这两个运算符重载,之后我们就可以进一步探究new和delete了。

4. new和delete的基本原理

代码:

#include<iostream>
using namespace ::std;
class Date
{
public:
	Date(int year)
	{
		cout << "Date" << endl;
		_year = year;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
};
int main()
{
	Date* p = new Date(1949);
	delete p;
	return 0;
}

①new对类对象

在这里插入图片描述

代码分析:

	Date* p = new Date(1949);

图解:
在这里插入图片描述

②delete对类对象

在这里插入图片描述

代码分析:

	delete p;

图解:
在这里插入图片描述
进入此句汇编代码:
在这里插入图片描述

拓展—— 深入理解delete[]和new[]

  • 讨论范围:类对象数组
  • 在我们创建一个类对象数组时,知道调用多少次构造函数,但在销毁时,我们怎么知道要调用多少次析构函数?
  • 换句话说——就是new[]做了什么,可以让delete[]省去这个找到有多少个对象的操作。

实验代码:

#include<iostream>
using namespace ::std;
int main()
{
	Date* arr = new Date[4]{1,2,3,4};
	delete[] arr;
	return 0;
}
  • 答案很简单,new[]在开辟数组的空间的前4个字节,放了数组的相关信息,有多少个对象,数组的大小等信息,这样delete[]就能通过指针来找到相关信息,从而进行操作。

那这样的代码呢?

#include<iostream>
using namespace ::std;
int main()
{
	Date* arr = new Date[4]{1,2,3,4};
	delete arr;
	return 0;
}
  • 这样delete将不知道调用几次析构函数,从而可能会出现内存泄漏,这是很可怕的现象。
  • 因此:new[] 和delete[]一定要配套使用。

对比 C和C++内存管理的用法

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

5. 定位new

  • 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

基本用法

#include<iostream>
using namespace ::std;
int main()
{
	Date* p2 = (Date *)operator new(sizeof(Date));
	new(p2)Date(1949);//如果有默认构造,括号可以不写。
	delete p2;
	return 0;
}
  • 一般我们会在池化技术中使用,就好比原来你家没有厕所,你上厕所,要去公共厕所,比较远,厕所还可能有人,但是现在你家门口造了厕所,别人要用还要通过你同意,这是不是方便了许多,池化技术与此雷同,也是为了提高效率

6. 内存泄漏

  • 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

看这样一段代码:

#include<iostream>
using namespace ::std;
int main()
{
	char* p = new char[1024 * 1024 * 1024];
	return 0;
}

这个程序在编译器上会导致内存泄漏吗?
答案是——不会的。因为编译器会在程序结束时,帮你释放这些空间,这就跟手机卡了,关机再重启的道理是一样的,但是如果是一个24*7小时不停的程序呢?比如服务器,如果存在内存泄漏,如果说大了很快就会被检查出来,但是如果一次只泄漏几个字节,那么这台服务器会越用越卡,最终导致服务器宕机——出事故了,那相关人员就得跟着遭殃…

  • 分类

1.系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
2.堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

那该如何解决内存泄漏呢?

1.养成良好的编程习惯,对开辟的空间即用即销毁。
2.合理利用检查内存泄漏的工具,通常这类工具比较昂贵。
3.利用RAII思想——资源获取即初始化,或者智能指针——充分地体现了RAII思想。
4.公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
5.私有内存管理库,就是公司内部实现的,这套库自带内存检查功能。

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

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

相关文章

Java数据类型之字符串

字符集/编码表 ex&#xff1a;ASCII码 字符char&#xff1a;单引号‘ ’引起来的单个字符 转义字符 \n 作用&#xff1a;换行&#xff0c;单引号引用&#xff01;&#xff01;&#xff01; 制表符 \t 作用&#xff1a;加上 \t 前面一共空8个 字符与编码的转换 1.直接输出字…

opencv_c++学习(十九)

一、图像间的距离变换 三种常用的距离计算方法&#xff1a; 欧式距离这里就不在解释。 街区距离&#xff1a;顾名思义&#xff0c;就类似于城市距离一样&#xff0c;并不是通过两点间的距离&#xff0c;而是我们从一个地点到达另一个地点的路程(横纵坐标差值之和)。 棋盘距离…

每日学术速递5.21

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Going Denser with Open-Vocabulary Part Segmenta 标题&#xff1a;通过开放式词汇部分分割变得更密集 作者&#xff1a;Peize Sun, Shoufa Chen, Chenchen Zhu, Fanyi Xiao, Pi…

代码随想录算法训练营第十三天|239. 滑动窗口最大值、347.前 K 个高频元素

滑动窗口最大值 题目链接&#xff1a;力扣 知识点&#xff1a;单调队列 解题思路&#xff1a; 需要一个队列&#xff0c;放进去窗口里的元素&#xff0c;然后随着窗口的移动&#xff0c;队列也一进一出&#xff0c;每次移动之后&#xff0c;队列告诉我们里面的最大值是什么…

Java基础-Java常用类1(包装类 + Object类)

本篇文章主要讲解Java的常用类 包装类Object类 希望能对你的复习以及面试有帮助,有错误请指正 , 感谢. 目录 包装类 Object类 Object 类的常见方法有哪些&#xff1f; 对象比较(hashcode和equals方法) 和 equals() 的区别 hashCode() 是什么 ? 有什么用&#xff1f; 那…

【数据分享】中国首套10米分辨率的建筑高度数据(tif格式)

建筑是城市最重要的构成要素&#xff0c;高密度高层数的建筑是城市区别于乡村的显著特征&#xff01;建筑数据也是我们在各项研究中都会使用到的数据&#xff01;之前我们分享过2020年全国90个城市市域范围的建筑体块数据&#xff08;可查看之前的文章获悉详情&#xff09;。 …

Ubuntu2004设置共享开发环境

我们都知道Linux操作系统是一个多用户的操作系统&#xff0c;由于大家在实际工作中很少接触到多用户环境&#xff0c;特别是在目前电脑硬件成本不断降低的情况下几乎每个从事IT行业的人员都有一台甚至多台个人PC&#xff0c;因此大家对多用户的理解并不深刻。 ChatGPT引燃了人…

【数据结构】堆堆堆堆堆!

目录 前言 树 树的概念 树的相关概念​编辑 树的表示 二叉树的概念 特殊的二叉树 ​ 二叉树的存储结构 堆 堆的建立(本篇以小堆为例&#xff0c;大堆实现方法一样&#xff09; 堆的结构定义 堆的初始化 堆的插入 堆的基础算法——向上调整算法 插入注意事项 堆的判…

Openai+Coursera: ChatGPT Prompt Engineering(三)

想和大家分享一下最近学习的Coursera和openai联合打造ChatGPT Prompt Engineering在线课程.以下是我写的关于该课程的前两篇博客&#xff1a; ChatGPT Prompt Engineering(一) ChatGPT Prompt Engineering(二) 今天我们来学习第三部分内容&#xff1a;推断(Inferring) 推断…

Android:IPC(进程间通信)机制

Android&#xff1a;IPC&#xff08;进程间通信&#xff09;机制 进程和线程 我们先来了解一些关于线程和进程基本的概念。 按照操作系统中的描述&#xff0c;线程是CPU调度的最小单元&#xff0c;同时线程是一种有限的系统资源。而进程一般指一个执行单元&#xff0c;在PC和…

(学习日记)AD学习 #2

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

npm ERR! code E404 在vscode安装插件时报错的解决方案

答主在配置commit代码提交规范时【即如下代码】遇到了以下问题 npm i cz-customizable6.3.0 --save-dev 出现了 npm ERR! code E404 npm ERR! 404 Not Found - GET https://registry.npmjs.org/vue%2fvue-loader-v15 - Not found npm ERR! 404 ……等报错情况 解决方案1 检查n…

SVN 导出改动差异文件

文章目录 SVN 导出改动差异文件应用场景/背景介绍具体操作方法 SVN 导出改动差异文件 应用场景/背景介绍 当然下面的两个场景介绍可能用分支管理都会有不错的效果&#xff0c;或者更优&#xff0c;只是记录一下思路&#xff0c;用什么还是看大家个人爱好啦 在开发过程中偶尔会…

nexus私服仓库maven-metadata.xml缺失导致的构建失败或者下载504

环境&#xff1a;maven项目&#xff0c;使用Nexus私服&#xff0c;jenkins实现代码的编译和打包。 问题分析思路&#xff1a;某周末前&#xff0c;jenkins上的编译打包任务一直正常工作&#xff0c;但周末后突然所有项目都编译失败&#xff0c;报错很一致都是Could not find a…

【牛客刷题专栏】0x30:JZ38 字符串的排列(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录 前言问…

局域网与城域网 - ARP 地址解析协议

文章目录 1 概述2 ARP 地址解析协议2.1 工作过程2.2 报文格式2.3 ARP 命令 3 扩展3.1 网工软考真题 1 概述 #mermaid-svg-CQnNvTP8xFoJsztk {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-CQnNvTP8xFoJsztk .error-…

plt、fig、axes、axis的含义

plt import matplotlib.pyplot as plt figure,axes与axis 如果将Matplotlib绘图和我们平常画画相类比&#xff0c;可以把Figure想象成一张纸&#xff08;一般被称之为画布&#xff09;&#xff0c;Axes代表的则是纸中的一片区域&#xff08;当然可以有多个区域&#xff0c;这…

剑指 Offer - 字符串合辑

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 题解目录 一、&#x1f331;[剑指 Offer 05. 替换空格](https://leetcode.cn/problems/t…

[笔记]C++并发编程实战 《二》线程管理

文章目录 前言第2章 线程管理2.1 线程管理的基础2.1.1 启动线程2.1.2 等待线程完成2.1.3 特殊情况下的等待2.1.4 后台运行线程2.2 向线程函数传递参数 前言 第2章 线程管理 本章主要内容 启动新线程等待线程与分离线程线程唯一标识符 好的&#xff01;看来你已经决定使用多…

使用压缩包安装jdk多版本并能领过切换

使用压缩包安装jdk多版本并能领过切换 1.下载2.解压包到指定位置3.使用pdate-alternatives 进行版本切换管理3.1. jdk173.2. jdk1.8 3.切换版本4.解决JAVA_HOME环境变量识别的问题 1.下载 官网的下载地址&#xff1a; 下载地址&#xff1a; jdk17: jdk1.8在当前页面的下面: …