C++初阶第七篇——C/C++的内存管理(C/C++动态内存分布+new和delete的用法和实现原理)

Posted 呆呆兽学编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初阶第七篇——C/C++的内存管理(C/C++动态内存分布+new和delete的用法和实现原理)相关的知识,希望对你有一定的参考价值。

⭐️1现在,我们已经结束了C++的一个小部分,我还要给大家介绍一些C++在C语言中做的一些补充。
⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/cpp-class-code

目录


🌏C/C++的内存分布

C/C++内存有六个区域 ,我们经常听到的有数据段代码段。还有两个分别是内核空间内存映射段
下面是几个说明:
:向下增长,非静态局部变量,函数返回值,参数列表,函数栈帧等(一般8M)
:向上增长,动态内存分配,手动申请
数据段:存储静态数据和全局数据
代码段:可执行代码,只读常量

🌏C语言中的动态内存管理

前面有博客讲到过,有兴趣可以去看一下:C语言之动态内存管理

🌏C++内存管理方式

方式: 通过newdelete操作符进行动态内存管理。C++可以继续使用C的内存管理方式,也可以用自己的内存管理方式。
下面用这两个操作符来处理自定义类型内置类型,并且和C的内存管理方式的效果进行对比。

  1. 内置类型
int main()

	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = (int*)malloc(5 * sizeof(int));

	// 对于内置类型,malloc和new没有本质的区别,只是用法不同
	// new 和 delete都是操作符
	int* p3 = new int;
	// int* p3 = new int(3); 
	int* p4 = new int[5];
	// int* p4 = new int[5] 1,2,3,4,5 ; C++98不支持初始化new的数组,C++11支持
	
	free(p1);
	free(p2);
	
	delete p3;
	delete[] p4;
	
	p1 = nullptr;
	p2 = nullptr;
	p3 = nullptr;
	p4 = nullptr;
	
	return 0;

对于内置类型,malloc和new没有本质的区别,只是用法不同

通过调试发现,对于内存类型,两者都不会对空间初始化,但是new可以通过一定的手段来进行初始化。

int* p3 = new int(3);
int* p4 = new int[5] 1,2,3,4,5 ; C++98不支持初始化new的数组,C++11支持

上面两种方式都是可以在开空间时,给它们来进行初始化。但是C语言中的malloc不可以。

  1. 自定义类型
class A

public:
	A(int a = 10)
		:_a a 
	
		cout << "A(int a = 0)" << endl;
	
private:
	int _a;
;

void test()

	// 对于自定义类型,new会调用构造函数,delete会调用析构函数,而malloc和free不会
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = (A*)malloc(sizeof(A) * 5);

	// new在堆上申请空间,然后调用构造函数初始化
	// delete先调用析构函数,然后释放空间给堆上
	A* p3 = new A;
	A* p4 = new A[5];
	// A* p4 = new A[5] 1,2,3,4,5 ;

	free(p1);
	free(p2);


	delete p3;
	delete[] p4;


	p1 = nullptr;
	p2 = nullptr;
	p3 = nullptr;
	p4 = nullptr;

对于自定义类型,new会先开空间,然后调用构造函数进行初始化,delete会调用析构函数来清理,然后释放空间,而malloc和free不会。观察下面的图片也可以发现。
总共有6次构造函数的调用。

总结:

  1. 对于内置类型,malloc和new没有本质的区别,只是用法不同
  2. 对于自定义类型,new会调用构造函数,delete会调用析构函数,而malloc和free不会

🌏C和C++在内存申请失败时处理方式的区别

面向对象的语言,处理错误的方式一般是抛异常,C++也是这样要求的 ( try catch)
面向过程的语言,处理错误的方式一般是返回值+错误码

  1. C语言
int main()

	size_t sz = 2;
	int* p1 = (int*)malloc(1024 * 1024 * 1024 * sz);
	if (p1 == nullptr)
	
		printf("%d\\n", errno);// errno-错误码
		perror("malloc fail");
		exit(-1);
	

	printf("%p", p1);
	free(p1);

	return 0;

运行结果如下:

我们可以发现,内存申请失败了,程序返回了错误码:12,原因是系统没有这么大的空间。

  1. C++
int main()

	size_t sz = 2;
	int* p1 = new int[1024 * 1024 * 1024 * sz];

	delete[] p1;
	return 0;

运行结果如下:

程序直接崩溃。

下面我们对错误进行抛异常处理:

int main()

	size_t sz = 2;
	
	char* p1 = nullptr;
	try
	
		// 进行检测 有错误就进入catch被铺获,try中错误代码后面也不执行
		p1 = new char[1024 * 1024 * 1024 * sz];
		cout << "检测无误" << endl;
	
	catch (const exception& e)
	
		cout << e.what() << endl;
	

	printf("%p", p1);
	delete[] p1;
	return 0;

🌏operator new和operator delete函数

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

operator new

operator new是通过malloc来开空间。相比malloc多了一个抛异常的功能。

总结:

  • operator new = malloc + 失败抛异常处理
  • new = operator new + 构造函数

operator delete

operator delete与delete无区别,它的出现主要是为了和operator new成对出现

🌏new和delete的实现原理

  1. 内置类型

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

  1. 自定义类型
  • new的原理
    先调用operator new函数申请空间,然后调用构造函数进行初始化
  • delete原理
    先调用析构函数清理资源,然后调用operator delete函数释放空间
  • new T[]原理
    先调用operator new[]函数完成N个对象的空间的申请,然后调用N次构造函数进行初始化
  • delete[] 原理
    先调用N次析构函数完成N个对象中资源的清理,然后调用operator delete[]函数释放空间

🌏定位new表达式

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式: new(空间指针)类型(类型初始化列表)

class B

public:
	B(int a = 0, int b = 0)
		:_a(a)
		,_b(b)
	
private:
	int _a;
	int _b;
;
int main()

	B* b = new B;
	new(b)B(2, 3);// 有参传参,无参不传

	return 0;

🌏malloc/free和new/delete的区别

从三个角度分析如下:

  1. 概念性质: malloc/free是函数,new/delete是操作符
  2. 使用方法: malloc需要手动计数申请空间的大小且需要将void*类型强转为对应类型,new后面跟着的是类型,只需要按类型申请即可。
  3. 使用效果: malloc申请的空间不会进行初始化,且申请失败是返回NULL,new申请的空间可以初始化,对应自定义类型会调用它的构造函数进行初始化,delete在释放空间之前会调用析构函数来清理资源。

🌏内存泄漏

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

注意: 内存泄漏对短期的运行程序危害不大,对长期运行的程序影响很大,会导致系统卡死等。

🌐总结

今天就简单介绍了C/C++中内存管理的相关知识,喜欢的话可以点赞和关注支持一下,欢迎大家点赞支持~

以上是关于C++初阶第七篇——C/C++的内存管理(C/C++动态内存分布+new和delete的用法和实现原理)的主要内容,如果未能解决你的问题,请参考以下文章

C++初阶C&C++内存管理

C++模板初阶 | 内存管理

C++初阶:内存管理C/C++内存分布及管理方式 | new/delete实现原理及operator new和operator delete函数

C++初阶:内存管理C/C++内存分布及管理方式 | new/delete实现原理及operator new和operator delete函数

Linux篇第七篇——进程地址空间(程序地址空间+虚拟地址空间)

C++从入门到入土第七篇:模板初阶