c++ 内存管理

内存分配方式

首先看一下内存是如何分配使用的
在这里插入图片描述

  1. 静态存储区域分配。
    内存在程序编译的时候就已经分配好了,也就是说已经编过地址了
  2. 在堆栈上分配 。
    在函数执行期间,函数内局部变量(包括形参)的存储单元都创建在堆栈上,函数结束的时候这些存储单元自动释放。这种内存分配方式的效率很高,一般不存在失败的风险,但是分配的内存容量有限,堆的容量很小,是兆(M)级别的,容易发生栈溢出。
  3. 在堆上分配。
    也叫做动态内存分配,程序在运行期间用malloc或new申请任意数量的内存,堆的容量很大,是G级别的,(比如递归时容易发生栈溢出,就可以在堆上模拟实现栈完成递归)。程序员自己决定释放内存的时间(使用free或delete)。动态内存的生存时间由程序员决定,使用非常灵活,但是也非常容易出错。
  4. 使用内存分配的一般原则是:
    如果使用堆栈和静态存储就能满足应用要求,就不要使用动态存储。这是因为在堆上动态分配内存需要其他的额外开销。主要原因如下。
    4.1 在堆上分配内存时,需要找到符合要求的空闲的连续的字节内存块。特别是在经过多次内存分配之后,堆会变得“千疮百孔”,出现大量的闲散的内存碎片,此时可能需要首先进行碎片合并,然后才能分配成功,在这种情况下内存分配需要很长时间。
    4.2如果动态分配失败,需要检查返回值或者捕获异常(try catch),这也需要额外的开销。
    4.3动态创建的对象可能被删除多次(比如在这个程序前一部分你已经释放过内存了,但是后来你又忘记了,就又释放了一次),甚至在删除后还会继续使用(还是前面那个例子,已经释放后还要继续使用那块内存空间)。如果发生这种情况,运行时会发生错误,或者出现“内存泄漏”的现象,这些问题是很难避免的,当代码量很大的时候,很容易出现这些意外情况。
  5. 代码段和数据段的区别和联系
    5.1 代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆积组成的。
    5.2 数据段:(也被称为数据区、静态数据区、静态区):数据段就是程序中的数据,直观理解就是C语言程序中的全局变量。(注意:全局变量才算是程序的数据,局部变量不算程序的数据,只能算是函数的数据)

c语言中的内存管理方式

1. malloc/realloc/calloc

1.1 malloc
在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址

int* p=(int*)malloc(sizeof(int)*10);//开辟40个字节的空间

1.2 realloc
在已有的基础上对内存空间进行调整,也就是进行扩容

int* p=(int*)malloc(40);//先开辟一块40个字节的空间
 p=(int*)realloc (p,400);//再把这块空间扩容到400个字节

1.3 calloc
和malloc的作用相识,开辟好空间之后会把这块空间的数值初始化成0

int* a = (int*)calloc(10, sizeof(int));//开辟一块40个字节的空间,并且把她们初始化成0

2.free

释放空间,为了避免野指针,free后要再把指针置成空(NULL)

free(p);//把空间还给操作系统
p=NULL://把指针置成null,防止再次访问

c++中的内存管理方式

1. new/delete

new是c++新引入的操作符,他的主要作用和malloc他们几个一样,都是动态开辟空间,但是new又做了一点小改进。

  • 对于自定义类型,如果使用new,会开辟空间+调用构造函数初始化。
  • 对于内置类型,如果使用new,和malloc的作用一样,只会开辟空间。

delete是c++新引入的操作符,他的主要作用和free相似,但是delete不仅会释放空间,还会自动调用析构函数。

  • 对于自定义类型,如果使用delete,会释放空间+调用析构函数。
  • 对于内置类型,如果使用delete,和free的作用一样,只会释放空间。

内置类型的例子

void test1()
{
	int* p1 = new int;
	//new一个int大小(4个字节)的空间
	int* p2 = new int(10);
	//new一个int大小(4个字节)的空间,并且初始化成10
	int* p3 = new int[10];
	//new一个40个字节的空间(10个int类型)
	int* p4 = new int[]{ 1,2,3,4,5 };//这里大括号里是数组个数,可以省略,就像初始化数组一样
	//new一个40个字节的空间的大小,并且初始化他们


	delete p1;
	delete p2;
	delete[]p3;
	delete[]p4;

}

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


自定义类型的例子

class Test
{
public:
	Test()
	{
		cout << "构造函数"<<"  " << this << endl;
	};
	~Test()
	{
		cout << "析构函数" << "  "<<this << endl;
	}
private:
	int x;
};

void test2()
{
	Test* p1 = new Test;
	Test* p2 = new Test[10];
	delete p1;
	delete []p2;

}

代码运行结果如下:
可以看出用new开辟空间,不仅会开辟空间,还会自动调用构造函数和析构函数。
在这里插入图片描述

在这里插入图片描述
总结:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。

2.operator new/operator delete

operator new /operator delete是系统提供的全局函数,而new/delete是一种操作符。
new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。


operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。operator new本质是封装了malloc。operator delete本质是封装了free。
⚠️注意:operator new/operator delete不是对new /delete的重载,他们就是独立存在的库函数。

说了这么多,用一句话总结一下:
当你用new开辟空间的时候,new会去调用oparator new函数,然后operator new又会去调用malloc开辟空间。

  • new=operator new+构造函数
  • operator new=malloc+失败了抛出异常
    对于delete也是同理
  • delete=operator delete+析构函数
  • operator delete=free

画个图理解一下
在这里插入图片描述

在这里插入图片描述

new/delete实现原理

内置类型


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


自定义类型

new
调用operator new函数申请空间 在申请的空间上执行构造函数,完成对象的构造
delete
在空间上执行析构函数,完成对象中资源的清理工作 调用operator delete函数释放对象的空间
new T[N]
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请 在申请的空间上执行N次构造函数
delete[ ]
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值