C++动态内存管理new,delete

C++动态内存管理new,delete

  • 1.C/C++内存分布
  • 2.C语言中的内存管理方式
  • 3.C++中的内存管理方式new,delete
    • 3.1C++中的内置类型new,delete
    • 3.2new,delete操作自定义类型
    • 3.3 new和delete匹配
  • 4. operator new与operator delete函数
    • 4.1new和delete底层实现
    • 4.2new和delete匹配,new []和delete []匹配
  • 5. 定位new表达式(placement-new)
    • 5.1定义
    • 5.2使用场景

所属专栏:C“嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️

1.C/C++内存分布

在学习C语言的时候我们有讲到过动态内存够管理部分C语言当中的动态内存管理在这一篇文章中我们也有详细了解到什么是动态内存,为什么要存在动态内存。

这里我们也做简单的回顾一下:

  • 在内存中为什么要存在内存区域划分呢?因为在我们向内存输入数据的时候,不同的数据有不同存储需求,而不同的内存区域就是为了满足对不同数据的存储需求而设计的。
    • 比如一些数据可能就是一个临时的数据,我们就可以存储到栈区。
    • 有些数据可能是动态使用的,需要时就创建,不需要是就销毁,就可以存到堆区。
    • 有些数据可能是要长期使用,整个程序期间使用,随时都可能需要使用的,这个时候就可以存放在静态区。
    • 也有一些数据是只读不修改的,比如常量,可执行代码,这些数据就可以存放在常量区。

我们可以用一张内存区域分布图来概括我们的内存:
在这里插入图片描述
说明:

  1. 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
    创建共享内存,做进程间通信。
  3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  4. 数据段–存储全局数据和静态数据。
  5. 代码段–可执行的代码/只读常量。

下面我们来做一些判断内存区域的题型:

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);
}
  1. 选择题:
    选项 : A.栈 ;B.堆 ;C.数据段(静态区) ;D.代码段(常量区)
    1.globalVar在哪里?____ ;2.staticGlobalVar在哪里?____;

    3.staticVar在哪里?____ ;4.localVar在哪里?____;

    5.num1 在哪里?____;

    6.char2在哪里?____ ;7.* char2在哪里?___;

    8.pChar3在哪里?____ ;9.* pChar3在哪里?____;

    10.ptr1在哪里?____ ;11.* ptr1在哪里?____;

  2. 填空题:
    1.sizeof(num1) = ____;

    2.sizeof(char2) = ____;3. strlen(char2) = ____;

    4.sizeof(pChar3) = ____; 5.strlen(pChar3) = ____;

    6.sizeof(ptr1) = ____;

正确答案:

一:1-5:CCCAA,6-11:AAADAB
二:40,5,4,4/8,4,4/8

第一题的前5个因该都没什么问题,问题可能出现在6-11题,我们讲解一下;
在这里插入图片描述

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

在学习C语言的时候我们有讲到过动态内存够管理部分C语言当中的动态内存管理在这一篇文章中我们也有详细了解到什么是动态内存,为什么要存在动态内存,如果有对知识点模糊不清的可以回顾一下。

3.C++中的内存管理方式new,delete

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

3.1C++中的内置类型new,delete

使用语法:

new + 类型(初始值)
delete + 类型

new + 类型[大小]
delete[] + 类型

int main()
{
	//开辟一个int类型的空间
	int* ptr1 = new int;
	
	//开辟一个int类型的空间并初始化为2
	int* ptr2 = new int(2);
	
	delete ptr1;
	delete ptr2;

	//开辟一个int类型的数组,大小为3
	int* ptr3 = new int[3];
	
	//开辟一个int类型的数组,大小为10,并初始化
	int* ptr4 = new int[10]{1,2,3,4};//没有被初始化的编译器会默认初始化为0
	
	delete[] ptr3;
	delete[] ptr4;
	return 0;
}

在这里插入图片描述
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用。

3.2new,delete操作自定义类型

class text
{
public:
	text(int val)
		:_a(val)
	{
	
	}
private:
	int _a;
};

int main()
{
	//C语言中的malloc
	text* t1 = (text*)malloc(sizeof(text));
	
	//C++中的new
	text* t2 = new text(1);
	
	free(t1);
	delete t2;
	return 0;
}

在这里插入图片描述

从上面malloc和new出来用法不一样之外没什么区别,唯一的区别就是malloc无法对自定义类型进行初始化,而new可以对自定义类型进行初始化,也就是说new做了两件事:开辟空间+构造函数。delete也做了两件事情:调用析构函数+释放空间。

3.3 new和delete匹配

class stack
{
public:
	stack(int capacity = 4)
	{
		cout << "stack()" << endl;
		_a = new int[capacity];
		_size = 0;
		_capacity = capacity;
	}

	~stack()
	{
		cout << "~stack()" << endl;
		delete[] _a;
		_a = nullptr;
		_size = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _size;
	int _capacity;
};

int main()
{
	stack* p1 = new stack(4);
	delete p1;
	return 0;
}

在这里插入图片描述

我们看一下上面一段代码,他的执行顺序什么呢?
在这里插入图片描述

  • 那如果我们把delete p1 换成 C语言中的free(p1)会怎么样呢?
    是不是free直接释放了对象的空间,而_a没有被释放啊,就会有内存泄漏。
    所以这里new一定要和delete匹配着使用。

4. operator new与operator delete函数

4.1new和delete底层实现

  • new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是
    系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过
    operator delete全局函数来释放空间。要注意的是operator new和operator delete不是重载 底层其实是封装了malloc和free。
  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来释
        放空间

官方库给定的封装:

/*
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) //最要注意的是这里,malloc开辟失败是返回0,而operate delete开辟失败不是返回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来释放空间的。
  • 而且我么也可以发现 operator new(size_t size)的参数是size_t size是一个大小值,operator delete(void* pUserData)的参数是一个对象。

我们也可以从汇编的角度来看一看:

class text
{
public:
	text(int a = 0)
	{
		_a = a;
		cout << "text()" << endl;
	}

	~text()
	{
		cout << "~text()" << endl;
	}
private:
	int _a;
};

int main()
{
	text* p1 = new text;
	delete p1;

	text* p2 = (text*)operator new(sizeof(text));
	operator delete(p2);

	return 0;
}

在这里插入图片描述
这里只调用了一次构造和一次析构。

  • text* p1 = new text;
    在这里插入图片描述
  • delete p1;
    在这里插入图片描述
  • 这里从汇编的角度我们就很清楚的可以看到,为什么new会做两件事情:开空间+调用构造,delete会调用析构+释放空间。
    底层其实是调用了malloc和free,只不过是被封装过的maloc和free。

我们也可以这样理解:

new = operator new + 构造函数
delete = 析构函数 + operator delete

4.2new和delete匹配,new []和delete []匹配

class text
{
public:
	text(int a = 0)
	{
		_a = a;
		cout << "text()" << endl;
	}

	~text()
	{
		cout << "~text()" << endl;
	}
private:
	int _a;
};

int main()
{
	text* p1 = new text[10];
	delete[] p1;
	return 0;
  • 我们看一下new[]是怎么开辟空间的。首先text类的大小是4个字节,开辟10个就是40个字节。这里可以类比一下开辟数组,数组是一个连续的内存,这里同样也是,10个类的物理内存是连续的,也就是说底层只会调用一次malloc,但是会调用10次析构函数。
    在这里插入图片描述
  • 如果按照我么正常的计算,这里内存因该会开辟40个字节的空间。
    我们进入汇编查看一下:
    在这里插入图片描述
  • 这里从监视窗口中我们看到的却是44个字节,对开了4个字节。
    我们在到内存中看一看:
    在这里插入图片描述
  • 至于为什么要多开一个个数的空间呢?
    是为了delete[]准备的,我们知道,new[10]是调用一次底层的malloc加上调用了10次构造函数,而之所以会调用10次构造函数是因为我们告诉编译器我们要开辟10个类内存大小。但是delete[ ]我们是没有告诉他有多少个的,它也就不知道要析构多少次,所以这块多出来的空间是为了提供给delete[]告诉它我们开辟了多少空间,让它析构多少次。
    在这里插入图片描述
  • 所以如果我们把delete[]换成delete或者free会怎么样呢?
    程序会崩掉,因为如果直接用delete或者free,编译器会认为你要释放的是一个对象,也就是从上面的p1开始释放,没有从多开的那块空间开始释放,也就只释放了部分的空间,而我们知道free是不能释放部分内存的,所以程序会崩。
    这里就说明了,我们要养成好匹配的习惯。

new和delete匹配。
new[] 和delete[]匹配。

5. 定位new表达式(placement-new)

5.1定义

  • 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
    使用格式:
    new (place_address) type或者new (place_address) type(initializer-list)
    place_address必须是一个指针,initializer-list是类型的初始化列表。

  • 问一个问题,构造函数能显示调用吗?
    答案是不能显示调用的,构造函数是创建对象时自动调用的。
    但是析构函数可以显示调用。
    所以C++中可以用定位new来显示调用构造函数

class text
{
public:
	text(int a = 0)
	{
		_a = a;
		cout << "text()" << endl;
	}

	~text()
	{
		cout << "~text()" << endl;
	}
private:
	int _a;
};

int main()
{
	text* p1 =(text*)operator new(sizeof(text));
	new(p1)text(1);
	//上面两个加起来就等于text* p1 = new text;

	p1->~text();
	operator delete(p1);
	//上面两个加起来就等于delete p1;
	
	delete[] p1;
	return 0;
}

5.2使用场景

  • 使用场景:
    定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如
    果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FISCO-BCOS 在ARM系统架构搭建节点(国密版)

问题&#xff1a; 使用 fisco-bcos v2.9.1 搭建一个节点&#xff0c;批量上链1000条数据&#xff0c;在上链200条-400条数据之间节点会出现异常&#xff0c;导致后面数据不能上链。 系统环境 操作系统&#xff1a;统信 查看系统构架 ld -version rootuos-PC:/# ld -version …

深入探索网络协议:揭开互联网运作的奥秘(建议收藏)

随着如今数字化时代的到来&#xff0c;互联网已经成为我们日常生活中不可或缺的一部分。然而&#xff0c;我们是否曾好奇过互联网是如何运作的&#xff1f;它是如何将我们与世界连接起来的&#xff1f;答案就在网络协议中&#xff0c;这是互联网背后的语言。 网络协议的作用和功…

网站更换IP的四大注意事项

1.对网站当中的数据进行备份 网站更换IP时可以将页面的数据库文件和站点文件通过下载工具在本地完成备份。 2.更换解析域名 从站点域名管理后台当中更换域名地址&#xff0c;改为新的IP地址。 3.确保IP安全 在用户更换IP前一定要确定IP是否安全&#xff0c;一旦IP存在不良…

python系统调用执行ping命令无法检测到超时情况(破案了:ping命令-W参数单位为s,我写了个1000)

文章目录 问题描述破案了&#xff1a;ping命令-W参数单位为s&#xff0c;我写了个1000。。。,,ԾㅂԾ,, 问题描述 我用了系统调用去执行ping&#xff0c;一开始用os.system()&#xff0c;有问题&#xff0c;后面用subprocess问题还是存在&#xff0c;后来我把这个改了&#xff…

CookieSession Redis 到JWT会话管理历史

单应用时期&#xff0c;通常使用 Cookies 和 Session 进行会话管理。 用户登录后&#xff0c;服务器创建一个唯一的会话标识符&#xff08;Session ID&#xff09;&#xff0c;将其存储在浏览器的 Cookies 中&#xff0c;并在服务端维护一个关联该标识符的会话对象。 这种方…

Woocommerce Private Store私人商店秘密商城插件,适合批发商店,会员制俱乐部

点击访问原文Woocommerce Private Store私人商店秘密商城插件&#xff0c;适合批发商店&#xff0c;会员制俱乐部 - 易服客工作室 WooCommerce Private Store插件是使 WooCommerce 私有的简单方法。密码保护您的整个 WooCommerce 商店并使其隐藏。 非常适合批发商店、会员制俱…

线程安全问题之死锁

作者简介&#xff1a; zoro-1&#xff0c;目前大二&#xff0c;正在学习Java&#xff0c;数据结构&#xff0c;javaee等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 线程安全问题之死锁 一个线程&…

基于减法平均优化器优化算法(SABO)-极限学习机(ELM)的时间序列预测模型matlab代码

极限学习机&#xff08;Extreme Learning Machine, ELM&#xff09;或“超限学习机”是一类基于前馈神经网络&#xff08;Feedforward Neuron Network, FNN&#xff09;构建的机器学习系统或方法&#xff0c;适用于监督学习和非监督学习问题。ELM在研究中被视为一类特殊的FNN&a…

亚马逊云科技 re:Invent 2023:科技前沿风向标,探索未来云计算之窗

文章目录 一、前言二、什么是亚马逊云科技 re:Invent&#xff1f;三、亚马逊云科技 re:Invent 2023 将于何时何地举行四、亚马逊云科技 re:Invent 2023 有什么内容&#xff1f;4.1 亚马逊云科技 re:Invent 2023 主题演讲4.2 亚马逊云科技行业专家探实战 五、更多亚马逊云科技活…

C++11【下】

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; 目录 &#x1f449;&#x1f3fb; 新的类功能类成员变量初始…

基于算能的AI边缘计算盒子,内置强悍TPU | 32TOPS INT8算力

边缘计算盒子 内置强悍TPU | 32TOPS INT8算力 ● 支持浮点运算的TPU平台盒子&#xff0c;支持32TOPSINT8&#xff0c;16TFLOPSFP16&#xff0c;2TFLOPSFP32高算力 ● 单芯片最高支持32路H.264 & H.265的实时解码能力 ● 支持国产算法框架Paddle飞桨&#xff0c;适配Caf…

Linux--初识和基本的指令(3)

目录 1.前言 1.指令 1.1 cat指令 1.2 echo指令 1.3 more 指令 1.4 less指令 1.5 什么时候使用less和more 1.6 head指令 1.7 tail指令 1.8 wc指令 1.9 与时间相关的指令 1.9.1 date指令 1.9.2 cal指令 1.10 16.find指令&#xff1a;&#xff08;灰常重要&#x…

Python学习路线 - Python语言基础入门 - 准备工作

Python学习路线 - Python语言基础入门 - 准备工作 初识PythonPython的优点 什么是编程语言Python环境安装Windows系统Python安装Python验证 MacOS系统Linux系统 第一个Python程序常见问题 Python解释器Python解释器概念Python解释器存放位置Python解释器运行".py"文件…

Android File Transfer for Mac:畅享强大的安卓文件传输工具

作为一款功能强大的安卓文件传输工具&#xff0c;Android File Transfer for Mac&#xff08;以下简称AFT&#xff09;为Mac用户提供了便捷快速的安卓设备文件管理体验。无论是传输照片、音乐、视频还是文档&#xff0c;AFT都能满足你的需求&#xff0c;让你的文件传输更加高效…

ffmpeg 查看本地摄像头和麦克风设备名称

命令 ffmpeg -list_devices true -f dshow -i dummy 2

【代码】基于多能互补的电热联供型微网优化运行(完美复现)matlab+yalmip+cplex

MATLAB代码&#xff1a;基于多能互补的电热联供型微网优化运行&#xff0c;电热需求响应参与的综合能源微网协同优化调度&#xff0c;引入电热储能解耦电热联系&#xff0c;破解以热定电 关键词&#xff1a;热电联供型微网&#xff1b;热电解耦&#xff1b;综合能源需求响应&a…

读书笔记:《Effective Modern C++(C++14)》

Effective Modern C&#xff08;C14&#xff09; GitHub - CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C》- 完成翻译 Deducing Types 模版类型推导&#xff1a; 引用&#xff0c;const&#xff0c;volatile被忽略数组名和函数名退化为指针通用引用&#…

关于安科瑞预付费系统在转供电情况下的应用-安科瑞 蒋静

1 前言 国家从2018年开始对转供电加价开展规范清理以来&#xff0c;已经出来了一系列政策&#xff0c;不仅包括专门针对转供电问题的政策&#xff0c;18-20年间还在每次降电价政策中突出强调了转供电主体不得截留降价红利的要求。从具体内容看&#xff0c;各地政策都鼓励一户一…

rtmp 协议详解

1. handshake 1.1 概述 rtmp 连接从握手开始。它包含三个固定大小的块。客户端发送的三个块命名为 C0,C1,C2&#xff1b;服务端发送的三个块命名为 S0,S1,S2。 握手序列&#xff1a; 客户端通过发送 C0 和 C1 消息来启动握手过程。客户端必须接收到 S1 消息&#xff0c;然后…

05-建造者模式-C语言实现

UML类图&#xff1a; 代码实现&#xff1a; #include <stdio.h> #include <stdlib.h>// 产品类 typedef struct {char* part1;char* part2;char* part3; } Product;// 抽象建造者类 typedef struct {void (*buildPart1)(void*, const char*);void (*buildPart2)(v…