一、关于c
我们知道在c语言中栈上申请空间方式有三种:
(1)void* malloc(字节):申请一块内存(无初始化)
(2)void* calloc(单个元素字节,个数):给出空间并初始化为0
(3)void* realloc(地址p,字节数):改变p的指针指向大小。
我们详谈realloc:
1)若p==NULL,他就和malloc是一样的作用。
2)若非空:-->newsize--->(1)newsize<=oldsize:直接返回p (2)newsize>oldsize--->1)大了一点点:返回p 2)大了很多(没找到返回NULL) ->(1)重新找一个新的newsize的字节内存块 (2)将旧空间的元素拷到新空间 (3)释放旧空间 (4)返回新空间p
释放空间我们采用free(指针):
常见的内存泄漏:
(1)内存忘记释放
(2)程序逻辑不清,以为释放了,实则没释放
(3)将堆破坏
(4)释放时传入的空间和释放时穿入的空间地方不相同。
我们如何进行检测:
(1)系统有一个自带工具
(2)自己写一个(难度较大)
(3)VLD工具
我们可以对这种问题进行预防:
(1)良好的代码规范
(2)智能指针
二、c++
可以继续使用c里面,同时自己又提出了:new/delete new[]/delete[]
(1) 对于内置类型,若没有匹配使用,不会造车内存泄漏。
(2)对于自定义类型,且析构函数显示给出,若没有搭配使用:(1)内存泄漏 (2)程序崩溃
若析构函数未给出,没有搭配使用,可能大致内存泄漏,不会崩溃。
详谈new:
做了两件事:
(1)调用void* operator new(szie_t size)函数申请空间-->这个函数的内部其实也是用malloc来完成空间申请的:
1)成功(返回地址)
2)失败(空间不足)---> 内部完成对空间不足做出的应对措施(提供了一个函数,用户自己来提供,检测是否存在已经申请的但不使用的空,没有就出错,抛一个异常)
(2)调用构造函数初始化对象。
详谈delete:
做了两件事:
(1)调用析构函数:清理对象中的资源
(2)调用void* operator delete(size_t size)释放空间,其也是对free的封装。
再来说说malloc内部干的事:
比如:int* p=(int *)malloc(sizeof(int)*10) 我们此时是申请了40个字节,但其实它内部需要管理,每个都会多申请36个字节。
详谈new T[N]:
做了两件事:
(1)调用void* operator new[] ,他又会调用operator new-->malloc
(2) 调用N次析构函数。 其实他在申请空间的时候会在多申请4个字节的空间,来记录对象的个数。(若没有析构函数,就不用记录,这里记录对象的个数,其实是为了在析构的时候知道应该析构多少次)。
详谈:delete[] pt
做了两件事:
(1)清理对象中的资源-->去空间前4个字节中取字节个数N,调用析构(N次) (倒着销毁,最后一个对象创建的最晚,生命周期短)。
(2)调用operator delete[] (从空间的起始位置释放)->operator delete-->free
总结:
1)无析构
malloc-->delete/delete[]->底层都是用free实现的
new-->delete/delete[]-->底层都是用free实现的
new[]-->delete/delete[]->底层都是用free实现的
2) 有析构
new->free:对象资源没有清理
->delete[]:崩溃(多释放4字节)
new[]->free:对象资源无清理&不会从起始位置进行释放,崩溃
->delete:只会销毁一个对象(N-1)个对象没有清理&不会从起始位置释放
因此我们在使用 new/delete new[]/delete[] 一定要搭配使用
对于void* operator new(size_t size)--->用户可以提供
(1)类成员函数 (2)普通成员函数 (3)库函数
定位new表达式:
在已存在的空间上执行构造函数。
假如我们要申请1000块空间,每次都会调用malloc,前面说过,每调malloc一次内部会多出36个字节,那此时就多出了36000个字节,这是极大的浪费。
因此我们一次性malloc一大块,每次拿的时候直接从malloc出来的那块空间里面拿就可以了。但是有一个问题,malloc出来的不会进行构造,因为我们需要采用new(地址) 类型,对其进行类的构造。此时它内部调用 operator new(size_t ,void* where ){return where;} 不需要指定大小,只需给位置就行。
两个问题:
(1)c库malloc和free等来动态管理内存,为什么c++还要定义new和delete运算符来动态管理内存呢?
答:因为c库里面的无法将对象正确构造和释放
(2)malloc/free和new/delete的区别:
1)malloc free是c库的函数,new delete是c++的操作符
2)malloc free只是动态分配和释放空间,new delete除了分配空间还会调用构造和析构函数
3)malloc 需要手动计算类型大小且返回值为void*,需要强转。new可以自己计算大小,返回相应类型的指针
4)malloc需要判空 new有自己的应对措施-->抛异常,不需要对结果进行判空,会捕获异常
5)malloc不可初始化 new可以
6)malloc在堆上 new不一定在堆上(可以自己写)