【C++】内存管理

c/c++中内存分布

c/c++ 中程序内存的区域划分:

在这里插入图片描述

分析下列代码中变量的存储地址:

int glob = 1;
static int staglob = 1;

int main()
{
	static int a = 1;
	int b = 2;

	int num1[10] = { 1,2,3,4 };
	char str1[] = "abcd";
	const char* pchar = "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);
	return 0;
}

存储结构分析
在这里插入图片描述

sizeof 与 strlen

sizeof:计算类型大小

在这里插入图片描述

strlen:计算字符长度,遇到 \0 结束

在这里插入图片描述

但是考虑一个问题:

为什么在 malloc 、calloc、realloc 了三个变量空间之后,只进行释放(free)了两次?

这就是我们接下来要讨论的一个问题。

c 语言中动态内存管理方式

malloc

void malloc( size_t size );
动态申请空间,但不进行初始化操作
由于返回值为 void
类型,因此在使用时需要进行类型强转
空间使用结束需要手动 free,否则会造成内存泄漏

malloc 申请到的空间内容为随机值:

在这里插入图片描述

calloc

void calloc( size_t num, size_t size );
动态申请空间,并将申请到的空间初始化为 0
由于返回值为 void
类型,因此在使用时需要进行类型强转
空间使用结束需要手动 free,否则会造成内存泄漏

calloc 申请到的空间内容会初始化为 0:

在这里插入图片描述

realloc

void *realloc( void *memblock, size_t size );
在原有的空间基础上进行空间的重新申请,并会释放回收原有的空间,不会进行初始化操作
空间使用结束需要手动 free,否则会造成内存泄漏

realloc 重新开辟新空间并会释放掉旧空间,不会进行初始化:

在这里插入图片描述

realloc 重新分配空间之后,会自动释放掉原有空间,因此只需要空间释放一次,并且在重新分配空间时会保持相应位置空间内容不变(扩容:原有大小内容不变,后序新空间为随机值;缩小容量:空间内容与原内容保持一致)

在这里插入图片描述

c++ 中动态内存管理

因为 C++ 兼容 C语言,因此在 C++ 语言当中,依旧可以使用 malloc 、calloc、 realloc 来进行动态内存管理,并且在使用结束之后手动 free 释放空间,避免造成内存泄漏。

new 与 delete

C++ 中也常通过 new 和 delete 来进行动态内存的管理:

new T:申请单个 T 类型的空间
new T[N]:申请N个T类型的一段连续空间

new T(a):动态申请一个 T 类型空间,并初始化为 a
new T[n]{…}:动态申请 n 个 T 类型的连续空间,并在 {} 中进行初始化操作

delete 变量名 :释放单个空间
delete[] 变量名:释放连续空间

在使用 new 与 delete 进行动态内存管理时候要注意对应:
申请单个空间 new T —> 释放单个空间 delete;
申请连续空间 new T[] ---->释放连续空间 delete[];

int main()
{
	int* p1 = new int; //申请单个 int 空间
	int* p2 = new int[10];  //申请 10 个 连续的 int 类型空间
	
	//初始化
	int* p3 = new int(10);
	int* p4 = new int[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };


	//空间的释放

	delete p1;
	delete[] p2;     //释放空间应与创建空间匹配----连续的空间
	delete p3;
	delete[] p4;      //释放空间应与创建空间匹配----连续的空间
	 
	
	return 0;
}

由此可见,C++中动态内存管理 new / delete 不需要进行类型的强转

自定义类型空间的动态分配

(1)使用传统的 C 语言 malloc / free 进行动态内存的管理:

class Test {
public:
	Test() :_data(1)
	{
		cout << "Test()" << this << endl;
	}
	~Test()
	{
		cout << "~Test()" << this << endl;
	}
private:
	int _data;
};

int main()
{
	Test* p1 = (Test*)malloc(sizeof(Test));       //单个 Test 类型空间
	free(p1);

	Test* p2 = (Test*)malloc(sizeof(Test) * 10);   //连续 10 个 Test 类型空间
	free(p2);
	return 0;
}

在这里插入图片描述

(2)使用 C++ 中 new / delete 进行动态内存管理:

class Test {
public:
	Test() :_data(1)
	{
		cout << "Test()" << this << endl;
	}
	~Test()
	{
		cout << "~Test()" << this << endl;
	}
private:
	int _data;
};

int main()
{
	Test* p1 = new Test;  //申请单个 Test 空间
	delete p1;   //释放单个空间

	Test* p2 = new Test[10]; //申请连续10个 Test 空间
	delete[] p2;   //释放连续空间

	return 0;
}

在这里插入图片描述

对于自定义类型的动态内存管理,倘若没有进行匹配释放空间则会造成程序崩溃或内存泄漏:

在这里插入图片描述

new 与 delete 的实现

在申请自定义类型的空间时,new 会调用相应的构造函数对空间进行初始化操作,delete 会调用相应的析构函数进行资源的清理:

在这里插入图片描述

operator new 与 operator delete

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

(一)operator new

new 申请空间,要么申请成功,要么报错,因此不需要进行判空。

new 申请新空间(自定义类型):
(1)内置类型直接调用 operator new 申请空间;
(2)自定义类型空间,先在堆上进行申请空间,其次调构造函数对空间进行初始化操作。

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

该函数实际上是通过 malloc 来申请空间(operator new 实际上是对 malloc 的封装),当 malloc 申请空间成功直接返回;申请失败,会进行相应不足应对措施:
检测是否是空间不足------若提供空间不足应对措施,则执行并继续 malloc;若没有提供则会发出 bad_alloc 类型异常。

在这里插入图片描述

通过反汇编来查看底层调用:

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

(二)operator delete

该函数实际上是通过 free 进行释放空间的。

在这里插入图片描述

delete 释放空间:
(1)若是内置类型,直接将空间释放;
(2)若是自定义类型空间,先调用析构函数进行资源的清理,其次将空间清理掉。

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

通过反汇编来查看底层调用:

在这里插入图片描述

内置类型创建连续空间,则会多次调用构造函数对空间进行初始化;
内置类型释放连续空间,则会多次调用析构函数对空间资源进行清理;

在这里插入图片描述

operator new 与 operator delete 支持重载,但一般情况下不用。

基本概念辨识

malloc/free 与 new/delete 区别 *****

相同点:

两者都是从堆上进行空间的申请,并且需要手动进行释放;

不同点:

(1)malloc/free 是函数(需要头文件),new/delete 是操作符(不需要头文件);

(2)malloc 申请的空间不会进行初始化(内容为随机值),new 可以进行初始化;

(3)malloc 申请空间时,需要手动计算空间大小并进行传参,new 只需要在其后跟上空间类型即可;

(4)malloc 返回值为 void* ,因此在使用时候需要进行类型强转,new 不需要;

(5)malloc 申请空间失败时返回 NULL,因此需要判空,new不需要—直接报错;

(6)申请自定义类型对象时,malloc/free 指挥开辟空间,不会调用构造函数与析构函数,而 new 在申请空间后会调用自定义类型对象的构造函数进行对象初始化,delete 在释放空间前会调用析构函数完成对象空间中的资源清理工作。

内存泄漏

内存泄漏:

指的是因为疏忽/错误造成程序未能释放已经不能使用的内存的情况。

内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的操作,因而造成了内存的浪费。

内存泄漏的危害:

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

内存泄漏分类:

(1)堆内存泄漏

指程序执行中依据需要分配通过 malloc、calloc、realloc、new 等从堆上分配的一块内存,用完后必须通过相应的 free、delete 进行删除。
假设程序设计错误导致这部分内存没有被释放,那么以后这部分空间将无法在被使用,会产生 Heap Leak.

(2)系统资源泄漏

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

欢迎评论留言哈~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值