CC++——深入探究动态内存管理

news2025/1/16 20:07:12

文章目录

  • 总述
  • C&C++程序内存区域划分
  • C++内存管理方式
    • 运用new/delete操作内置类型
    • new和delete操作自定义类型
  • operator new与operator delete函数
  • new和delete==操作符==的实现原理
    • 自定义类型
  • malloc/free 和 new/delete 的区别

总述

俗话说,没有理解过底层的c&c++程序员一定不是个优秀的程序员(doge),因此,在本篇博客中,将会大体地讲述一些c&c++程序内存管理的部分知识,并且再浅谈一下c++中是新增的操作(new,delete)如何进行动态内存管理的。

C&C++程序内存区域划分

首先我们需要了解一下C/C++程序中的数据是如何进行分布的,看下面这张图:
在这里插入图片描述

  1. 又叫做堆栈,用于存放非静态局部变量/函数参数/返回值等等,栈是向下生长的(朝内存地址减小的方向生长)
  • 在c/c++内存分配中,对于栈来说其数据是通过编译器自动进行管理的,无需我们手动控制,编译器会自动释放内存
  • 栈的空间很小,一般只有几MB的大小。
  1. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间的通信。
  2. 用于程序运行时的动态内存分配,堆是向上生长的(朝内存地址增大的方向增长)

区别于栈,堆中的内存管理需要程序员控制,因此需要避免内存泄漏的问题。

  1. 数据段(静态区): 存储全局数据和静态数据
  2. 代码段(常量区): 存储可执行的代码/只读常量

C++内存管理方式

在C语言中,我们进行动态内存管理是用<stdlib.h>库文件中的malloc/calloc/realloc/free进行的,这些方式在C++中当然可以继续使用,但是有些地方就显得无能为力了,而且使用起来比较麻烦,因此C++也制作了一套自己的内存管理方式: 通过new和delete操作符进行动态内存管理

运用new/delete操作内置类型

看下面代码:

void Test()
{
	//动态申请一个int类型空间
	int* ptr = new int;
	//动态申请一个int类型的空间并将其初始化为10
	int* ptr2 = new int(10);
	//动态申请10个int类型的空间
	int* ptr3 = new int[10];
	
	//释放空间
	delete ptr;
	delete ptr2;
	delete ptr3;
}

是的,使用方法就是 new 类型名,在类型名之后括号内的内容表示对其进行初始化,而[]表示初始化对象的个数。

**注意:**申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],切记一定要匹配起来使用!!!

new和delete操作自定义类型

对于自定义类型,new/delete和malloc/free差别确实不大,但是对于自定义类型来说,可以说new/delete简直是为自定义类型量身定做的!

看下面的代码:

#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
private:
	int _a;
	int* _parr;
public:
	A(int a = 0)
		:_a(a)
		,_parr(new int[10])
	{
		cout << "A(int a = 0)" << endl;
	}
	~A()
	{
		delete[] _parr;
		cout << "~A()" << endl;
	}
	void set()
	{
		_parr = new int[10];
	}
};

int main()
{
	//new/delete和malloc/free最大的区别就是
	//对于自定义类型除了开空间还会调用构造和析构函数


	A* Pa1 = (A*)malloc(sizeof(A));
	//malloc不会自动调用构造函数,必须得自己手动实现一个
	Pa1->set();

	A* pa2 = new A(10);
	//new支持分配空间时同时初始化

	A* pa3 = new A;
	//若申请空间时不进行初始化,将会调用默认构造函数

	Pa1->~A();
	//使用free之前需要自己调用析构函数,防止内存泄露
	free(Pa1);

	delete pa2;
	delete pa3;
	//使用delete在释放空间之前会先调用pa2的析构函数
	return 0;
}

该代码运行结果如下:
在这里插入图片描述

是的,在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会

operator new与operator delete函数

new 和delete 是用户进行动态内存申请和释放的操作符operator new 和 operator delete是系统提供的全局函数,new在底层调用operator new来申请空间,delete在底层调用operator delete全局函数来释放空间。
下面是库中operator new和 operator 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_FINALLY
	
	return;
 }
  /*
free的实现
 */
#define free(p)  _free_dbg(p, _NORMAL_BLOCK)

通过观察上述两个全局函数的实现知道,operator new实际上也是通过malloc来申请空间,如果malloc申请成功就直接返回,否则执行用户提供的空间不足的应对策略,如果用户提供该策略就继续申请,否则抛异常。operator delete最终也是通过free来释放空间的。


可能有人会问,为什么不直接底层用malloc和free来实现new和delete操作符,而还要再封装一层operator new和operator delete函数呢?

那是因为c++语言中对于程序运行中出现的问题采用的是抛异常的方法,而对于c语言内置的malloc和free函数,其在内存申请失败仅仅返回空指针,这有点不符合c++的要求,所以才需要在malloc的基础上再次进行了封装。

new和delete操作符的实现原理

对于内置类型来说,new/delete和malloc/free基本是相似的,需要注意的一点是:
new/delete申请和释放的是单个元素的空间,而new[]和delete[]申请的是连续空间。


自定义类型

  • new的原理
    1. 调用operator new函数申请空间
    2. 在申请的空间上执行构造函数,完成对象的构造
  • delete的原理
    1. 在空间上执行析构函数,完成对象中资源的清理。
    2. 调用operator delete函数释放对象的空间。

在这里插入图片描述

  • new T[N]的原理
    1. 调用operator new[]函数,在operator new[]中世纪调用operator new函数完成N个对象空间的申请
    2. 在申请的空间上执行N次构造函数
  • delete[] 同理

通过观察反汇编,可以看到new操作符确实调用了operator new函数和构造函数
在这里插入图片描述

malloc/free 和 new/delete 的区别

最后,我们来总结一下这两类的区别把!

共同点: 都是从堆上申请空间,并且需要用户手动释放。

不同点:

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

以上便是所有区别啦,对于这些区别,大家一定不要死记硬背,而是要理解的去记忆,这样才能有更好的效果!


对于c&c++内存管理方面的知识就到此结束啦!如果博主有哪些地方没有讲明白或者有错误的话,欢迎评论区指出哦!😘

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

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

相关文章

性能测试——服务端监控工具nmon

这里写自定义目录标题 一、性能监控工具nmon介绍二、nmon可监控的数据类型三、nmon特点四、Linux下安装1、检查安装环境2、下载nmon3、解压 五、运行nmon1、启动nmon2、常用nmon快捷命令3、nmon命令行参数4、命令行例子5、重点 六、查看 nmon 监控结果1、nmon_analyser 介绍2、…

AI:Vue2和Vue3的对比

1. 什么是Vue.js以及Vue.js在前端开发中的重要性。 Vue.js是一个遵循MVVM&#xff08;Model-View-ViewModel&#xff09;模式的前端JavaScript框架&#xff0c;它采用了双向数据绑定和组件化的思想&#xff0c;使得前端开发变得更加简洁、高效、可维护。Vue.js由中国工程师尤雨…

chatgpt赋能Python-python_dlib

Python Dlib&#xff1a;从入门到实践 Python Dlib 是一个用于人脸检测、跟踪和姿态估计的高性能C库&#xff0c;提供 Python 接口方便调用。它是一个开源项目&#xff0c;被广泛应用于计算机视觉、图像处理和机器学习等领域。本文将从介绍Dlib的原理、特点以及应用场景等方面…

Rshiny基本函数(交互式web应用)

Rshiny基础函数 引言shiny包的下载shinyApp的构成ui的设计函数HTML类比img函数无法加载图片控件函数ui设计的输出对象 server总结 引言 R语言shiny包可以帮助我们轻松的制作交互式的web应用&#xff0c;并且可以搭建为独立的桌面可执行程序&#xff0c;非常方便制作和分享&…

人工智能(柴玉梅)第四章课后部分习题答案(仅供参考)

注释部分只是复习用&#xff0c;不是答案 第五题 注释&#xff1a;便于复习 本来想搜个类似的&#xff0c;结果发现r1这种人家都带括号&#xff01;&#xff01;&#xff01;额&#xff0c;没括号那我就从前往后算了&#xff0c;按顺序算准没错~ CF&#xff08;H&#xff09;的…

Python logging使用

目录 logging模块 logging核心组件 logger handler StreamHandler&#xff1a;把日志内容在控制台中输出 FileHandler&#xff1a;把日志内容写入到文件中 filter formatter 注意日志级别的继承问题 logger.exception 上述样例的整体代码 日志的配置文件及其模板 lo…

用Python爬取了三大相亲软件评论区,结果…

小三&#xff1a;怎么了小二&#xff1f;一副愁眉苦脸的样子。 小二&#xff1a;唉&#xff01;这不是快过年了吗&#xff0c;家里又催相亲了 ... 小三&#xff1a;现在不是流行网恋吗&#xff0c;你可以试试相亲软件呀。 小二&#xff1a;这玩意靠谱吗&#xff1f; 小三&#…

使用Gradle7.6.1 + SpringBoot3.0.2 + java17创建微服务项目(学习)

这是一个大胆的决定 这里是导航 技术栈开发工具一、创建gradle父子项目&#xff08;deity&#xff09;1.0 简单流程示意1.1、IDEA中主要图示1.1.1 项目结构图1.1.2 IDEA中 Gradle配置 1.2、deity父项目build.gradle文件1.3、deity父项目settings.gradle文件1.4、子项目build.g…

计算机底层知识

汇编语言&#xff08;机器语言&#xff09;的执行过程 汇编语言的本质&#xff1a;机器语言的助记符 其实他就是机器语言 计算机通电->CPU读取内存中程序&#xff08;电信号输入&#xff09; ->时钟发生器不断震荡通电 ->推动CPU内部一步一步执行&#xff08;执行多…

elementUI,自定义表头,多层级表头,表头合并,行内容一致的合并行

先上效果&#xff1a; 1.自定义表头&#xff1a; 通过设置 slot"header" 来自定义表头。 slot-scope"scope" 这一行千万不要因为没有再template中使用到scope&#xff0c;vscode报红而删除&#xff0c;这会导致input框不能输入任何内容&#xff01; &l…

软考网工计算题总结(一):总共27类题型,进来复习啦!

题型一&#xff1a; 1.地址编号从80000H到BFFFFH且按字节编址的内存容量为&#xff08;5&#xff09;KB,若用16KX4bit的存储芯片够成该内存&#xff0c;共需&#xff08;6&#xff09;片。 (5)A.128 B.256 C.512 D.1024 (6)A.8 B.16 C.32 D.64 【答案】B C 【解析】本题…

写一个自己的MyGPT app

chatGPT大火之后&#xff0c;国内外一众玩家撸起袖子热火朝天干了起来。 借助开源的GPT可以轻松的拥有自己的专属GPT&#xff0c;装装逼还是很好用的&#xff0c;也算赶一下chatGPT的风口。 这里使用ANYGPT&#xff0c;打造自已的GPT&#xff0c;AnyGPT API 开发者文档 语雀…

【STM32】基础知识 第十六课 窗口看门狗 WWDG 深入浅出

【STM32】基础知识 第十六课 窗口看门狗 WWDG 深入浅出 概述窗口看门狗 (WWDG)WWDG_SR 状态寄存器WWDG 配置与使用使用 WWDG 进行故障检测案例 概述 在嵌入式开发中, 可靠性和稳定性是至关重要的. 这就是为什么许多单片机, 比如 STM32, 提供了窗口看门狗 (Window Watchdog, WW…

redis 源码记录

正好最近时间富裕&#xff0c;看一看redis源码&#xff0c;简单的记录一下。也有可能说的是不全面的&#xff0c;自行斟酌&#xff0c;只能通过debug来简单的梳理本次测试流程&#xff0c;而不是全量的覆盖的所有测试用例&#xff0c;时不时更新&#xff0c;争取一天学一点点。…

基于Web的水果蔬菜销售系统的设计与实现(论文+源码)_kaic

摘 要 随着互联网经济与技术进程的不断推进&#xff0c;网上购物方式受到公众的普遍关注和欢迎。传统的樱桃线下销售模式缺陷不断暴露&#xff0c;难以满足公众不断加快的生活节奏和生活方式的需求。本文根据目前大众的实际需要&#xff0c;根据网上商城的特点、现状以及主要功…

Java 与排序算法(3):插入排序

一、插入排序 插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的基本思想是将待排序序列分为已排序区间和未排序区间&#xff0c;然后每次从未排序区间取出一个元素&#xff0c;将其插入到已排序区间的合适位置中&#xff0c;使得插入…

【SpringBoot】八:Web服务---WebMvcConfigurer

文章目录 1.WebMvcConfigurer简介2. 页面跳转控制器3. 数据格式化4. 拦截器4.1 一个拦截器4.2 多个拦截器 1.WebMvcConfigurer简介 WebMvcConfigurer是SpringMVC xml配置文件的JavaConfig实现方式。 2. 页面跳转控制器 创建 SpringMVC 配置类: 3. 数据格式化 Formatter&…

计算机组成原理-存储系统-主存储器(芯片)和CPU连接

目录 一、SRAM和DRAM芯片 DRAM&#xff1a;栅极电容存储信息 SRAM&#xff1a;双稳态触发器存储信息 区别​编辑 二、ROM芯片 三、主存于CPU的连接 位扩展法 字扩展法 字位同时扩展法 译码器 四、双端口RAM和多模块存储器 4.1双端口RAM 4.2多模块存储器 4.2.1单体多字…

pthread多线程: 创建最简单的线程

文章目录 1. 目的1.1 不使用 Pthread 的情况1.2 使用 Pthread 的情况1.3 使用 Pthread 的好处 2. Pthread 创建线程的 API2.1 环境2.2 pthread_create()2.3pthread_join() 3. 创建最简单的线程3.1 要点3.2 代码 4. 创建多个子线程4.1 要点4.2 代码 5. 总结 1. 目的 Pthread 提…

计算机组成原理-存储系统-基本概论及组成

目录 一、存储器的层次化结构 二、存储器分类 存储介质 存取方式 信息可改性 三、性能指标 四、主存储器基本组成 基本的的半导体元件及原理 存储芯片的基本原理 一、存储器的层次化结构 二、存储器分类 存储介质 半导体存储器(主存&#xff0c;cache)、磁表面存储器(…