阁下何不乘风起,扶摇直上九万里。 qi fei
目录
operator new 与 operator delete函数
内存管理
分区介绍
1.栈区:
又叫堆栈,存放非静态局部变量/函数参数/返回值等等,栈相对来说是向下增长的。
2.内存映射段:
是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享来共享内存,做进程间通信(这个比较复杂,现在先不展开,了解即可)
3.堆:
用于程序运行时动态分配内存,堆是可以向上增长的。
4.数据段:
储存全局数据和静态数据
5.代码段:
可执行的代码/只读常量
补充:
栈区的空间较小,而堆区的空间很大。
C内存管理(简略回忆)
C语言中,内存管理总共有四种函数,malloc、calloc、realloc、free。
其中malloc和calloc是开辟空间的函数,malloc开辟时不会对开辟的空间进行初始化,calloc开辟时会将开辟的空间按内存置0,记得开辟较大空间时要检查是否开辟成功。
realloc作用是扩容,只能对malloc或calloc开的空间进行扩容,分为原地扩容和异地扩容。
原地扩容:如果在原来开辟的空间后有足够的空间,就会返回原地址,叫原地扩容。
异地扩容:如果在原来开辟的空间后没有足够的空间,就会一块新空间,将原空间数据拷贝过来,然后释放原空间,返回新地址,叫异地扩容。
free是释放,只能释放动态开辟的空间,记得每次动态开辟后要进行释放,否则可能会造成内存泄漏。(虽然一般程序结束时操作系统也会回收内存,在程序运行时会有内存泄漏等诸多问题)
------------------------------------
C++内存管理
new(开辟空间) delete(释放空间)
在C语言中,动态开辟空间需要计算字节大小,而C++中不需要。
内置类型
p1为一个int大小的空间 ,new + 类型
p2一个大小为5的数组, [n]为要开辟n个
p3为一个int大小的空间,并初始化为5, (n)为要初始化的值
p4为一个大小为8的数组,并进行初始化, { } 用来连续初始化,如果未给足够的初始化值,则无对应值的位置置为0
注:1.new出来的空间默认是不会初始化的。
2.申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[] ,注意要匹配着使用。
自定义类型
new和delete对于自定义类型会先开空间然后调用默认构造函数来初始化,没有默认构造会报错。
对于连续多个类型初始化要用{(),(),()}来初始化,只用()要么为一个参数要么为逗号表达式还是一个参数。
异常
new开辟失败后不会像malloc和calloc一样返回空指针,而是抛出一个异常,而异常必须被捕获,否则会报错,使用try + catch来捕获,这个捕获也比较复杂,所以暂且搁置,以后会总结到。
operator new 与 operator delete函数
这两个函数是系统提供的全局函数,不是重载,new在底层通过调用operator new来申请空间,delete在底层通过调用operator delete来释放空间。
operator new
其实这个函数使用的是malloc来开辟空间
operator delete
其实这个函数使用的是free释放空间
所以它们就是malloc和free的封装,在malloc的基础上将开辟失败返回空指针改为了抛异常。
补充QeQ
new单个自定义类型空间时:new = operator new + 默认构造函数
连续个时:new = operator new循环 + 多个默认构造函数
delete单个自定义类型空间时:delete = 析构函数 + operator delete
连续个时:delete = 多个析构函数 + operator delete循环
匹配问题
匹配指malloc和free匹配,new和delete匹配,连续空间和连续空间匹配,单个空间和单个空间匹配。
强调:
如果乱搭配不一定会报错,对于内置类型乱搭配很少会有错误,但对于自定义类型来说,就会导致不少错误。 (不过建议最好搭配使用,既清晰也方便他人观看)
自定义类型推荐使用new和delete,方便快捷,但要在开辟连续空间时用匹配的delete来释放,因为只用delete是只会调用析构一次,然后就释放了,但构造函数会在operator new开辟的空间进行对象的初始化,这些空间需要自定义类型自己的析构函数才能正确释放,所以new的空间就不能被正确的释放,导致内存泄漏。
在VS及某些编译器上,new在自定义类型时,会多开几个字节的空间放在最前方,但返回的还是要开的大小的空间的首地址,那么,这几个字节空间用来存什么呢,存的是要开的对象个数,也就是[ ]中的数值,用来告诉与它匹配的delete[ ]析构的次数,因为new了多少个对象就构造了多少,自然就要析构多少。
总结下:new和delete匹配,operator new一下,构造一下, 然后析构一下,operator delet一下。
new[ ]和delete[ ]匹配,operator new 循环,构造N下,然后析构N下,operator delete 循环,
完美释放,无内存泄漏。
定位new
定位new表达式是在已经分配的原始内存空间中调用构造函数进行初始化一个对象。
就像是定位到p1这个空间去进行构造。
但其实平常我们要实例化一个对象只需直接用new 类型 就好了,定位new不用在这里。
谈到定位new的使用场景,就要提到池化技术(内存池、线程池、连接池等),像内存池就是向操作系统要一块未初始化的空间,用来供给使用,定位new正好用在这里。
内存池简介:对于要频繁申请空间的时候,一次申请一大块空间,作为一个储蓄池,让每次使用时都能从这个池子里申请,而不是频繁的向操作系统申请,降低效率。
------------------------------------
总结
malloc/free和new/delete的共同点为:
都是从堆上申请空间,并且需要用户手动释放。
不同点:
1.malloc和free是函数,而new和delete是操作符。
2.malloc申请的空间不会初始化,new是可以去初始化。
3.malloc申请空间时,需要手动计算大小并传过去,new不需要,new只需在后面跟上空间的类型,如果是多个对象,[ ]中指定对象个数即可。
4.malloc的返回值为void* ,在使用时要强制转化,new不需要,因为new已经给类型并且按照类型开空间了。
5.malloc申请空间失败时,返回的是NULL,因此使用时要assert断言判空或if语句+perror判空,但new不会返回NULL,只会抛异常,只需要捕获异常。
6.申请自定义类型空间时,malloc只能开辟空间,而new既可以开辟空间,又可以调用自定义类型的析构函数进行对象的初始化;free只能释放空间,而delete既可以调用自定义类型的析构函数将对象析构,完成空间中资源的正确清理,又可以将空间释放。
小知识
1.四年一闰,百年不闰,四百年又一闰,为什么天数有差距呢,因为一年实际测算又365天5小时48分46秒,为了补齐天数,每四年加一天,也就是在二月加了一天,但因为它不是正好比365天多6小时,所以四年加一天还是不够,就百年再加一天,而百年加一天加多了,就四百年不加。
2.编译器可能会对无意义的代码进行优化,不去执行它。比如:a+b; 不用变量接收,也不进行操作,就是无意义。编译器直接忽略不管。