目录
Operator new和operator delete函数
C/C++内存管理
C/C++内存管理分布图:(从语言的角度来看,数据段也称为静态区,代码段也称为常量区)
内存大小:理论上,32位计算机内存上限为4GB;64位计算机内存上限可达256T,实际上我们使用计算机一般为8G或16G或者32G,不会很大。
栈和堆细节:
栈区先定义的变量放到栈底,地址高,后定义的变量放到栈顶,地址低,因此是栈是向下生长的,堆区则相反。
栈区主要存在局部变量和函数参数,其空间的管理由编译器自动完成,无需手动控制,堆区是自己申请。
堆大小受限于操作系统,而栈空间一般由系统直接分配。
栈一般是进行静态分配的,但也可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放,而堆无法静态分配,只能动态分配。
在C语言中我们用malloc/calloc/realloc和free来在堆上管理堆上的空间,但在c++中进行了更新。在C++中我们用New和delete来管理堆上的空间。
Malloc和new的最大差别是:C语言malloc失败后会返回NULL,C++New失败后会自动报异常。
注意:使用malloc是需要头文件malloc.h的,只是平时这个头文件已经被其他头文件所包含了,用的时候很少单独引入。
案例:变量在内存中到底会在哪?
请填写下列问题:
globalVar在哪里?__静态区__ staticGlobalVar在哪里?_静态区___
staticVar在哪里?__静态区__ localVar在哪里?_栈___
num1 在哪里?_栈___
char2在哪里?_栈___ * char2在哪里?__栈_
(用数组存储字符串,会在代码段生成一串字符,然后到栈上开辟一段空间,把内容拷贝进去)
pChar3在哪里?_栈___ * pChar3在哪里?_代码段(常量区)___
(用指针则是存放 代码段内存放字符串的地址,所以解引用地址后会在代码段)
ptr1在哪里?__栈__ * ptr1在哪里?_堆___
(prt1变量是在栈区创建的,所以在栈上,但申请的空间在堆上)
sizeof(num1) = __40__;
sizeof(char2) = _5___; strlen(char2) = __4__;(sizeof(数组)就是整个数组的大小)
sizeof(pChar3) = _4/8___; strlen(pChar3) = _4___;(sizeof(指针)则是看几位计算机)
sizeof(ptr1) = __4/8__;
New和delete
创建单个对象:new 类型(内容);创建数组:new 类型[大小]{内容},开辟类对象的数组时(该类对象有用户写的析构函数,默认生成的不行),会在前面额外开辟4字节空间,用于存放类对象数组有多少个元素,让delete数组时,调用类的析构函数时知道要调用几次。C++把new当做一个操作符而不是函数。
Delete也分为两种,释放单个空间:delete 对象名;释放一个数组:delete[] 对象名。
New和delete的使用方式如下:
在C++中,我们new一个类对象,编译器会自动调用该类的构造函数。这样就不需要像C语言malloc一个空间然后赋值那么麻烦。Delete也会调用该类的析构函数。
注意:new和delete一定要匹配使用,即new数组要用delete数组来释放。
如果不匹配使用,对于内置类型和没有自己写析构函数的类一般不会出错(默认生成析构函数是系统合成的没有作用的析构函数,所以系统什么也不做,只用释放空间,内置类型同理)
对于有自己写析构函数的类,delete会调用析构函数,而new数组本来应该匹配delete数组来释放,让编译器调用多次析构函数,但如果我们没有匹配使用,如下图,只用了delete a1,编译器只会调用一次析构函数,编译虽然能通过,但是运行会出断言错误。
Operator new和operator delete函数
Operator new和operator delete函数是系统提供的全局函数,它们实际上是new和delete的底层代码,new底层会调用operator new函数来申请空间;delete底层调用operator delete函数来释放空间。
Operator new底层代码也是通过malloc来开辟空间,即对malloc的封装,如果空间不够,则判断一下malloc是否返回NULL;如果申请内存失败了,这里会抛出bad_alloc类型异常,也就是new失败之后返回的异常。Operator delete同理,是对free的封装。
总之,operator new不是为我们准备的,而是为new准备的,主要是用来返回开辟空间异常的。
New和delete的原理
对内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
对自定义类型
New:
调用operator new函数申请空间,然后在申请的空间上执行构造函数,完成对象的构造。
Delete:
在空间上执行析构函数,完成对象中资源的清理工作,然后调用operator delete函数释放对象的空间
New[N]:
调用operator new[]函数,在operator new[]中实际调用operator new函数完成对N个对象空间的申请,在申请的空间上执行N次构造函数。
Delete[]:
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。(N是多少编译器会知道,不需要填),然后调用operator delete[]释放空间,实际上在operator delete[]中会调用operator delete来释放空间。
定位new
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。使用方法为:new(指针,指向开辟的空间)类型(要初始化的值),使用情况如下图所示:
New/delete和malloc/free的区别
区别:
- malloc和free是函数,new和delete是操作符。
- malloc申请的空间不会初始化,new可以初始化。
- malloc申请空间时,需要手动计算空间大小并传递;new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可。
- malloc的返回值为void*, 在使用时必须强转;new不需要,因为new后跟的是空间的类型。
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空;new不需要,但是new需要捕获异常。
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。