跟着侯捷老师冲啊!
new 和 delete编译
new被编译成什么
Complex* pc = new Complex(1,2)
new:先分配memory,再调用ctor
编译器转化为:
- void* mem = operator new( sizeof(Complex) );
分配内存,其内部调用了malloc(n) 来分配内存 - pc = static_cast<Complex*>(mem);
转型 - pc->Complex::Complex(1,2);
构造函数 ->Complex::Complex(pc,1,2)
delete被编译成什么
Complex* pc = new Complex(1,2);
...
delete pc;
delete:先調用dtor,再释放内存
编译器转化为:
- Complex::~Complex(pc);
调用析构函数 - operator delete(pc);
释放內存。内部调用 free(pc)
动态分配内存大小
动态分配一个Complex或者String
上图每一格是4字节
Complex大小:
- 最左图是调试模式下的复数内存大小,左2是非调试模式下的内存。多出的调试内存是灰色块,可以看到,调试模式下多了
8*4 + 4 = 36字节大小 - 上下各带了4字节的cookie,总共
4*2 = 8字节,作用是用来,红色 - Complex本身大小是8字节(re和im两个double),浅绿色。
所以最左侧,大小为 8 + (32+4) + (4*2) = 52。在VC编译器下,内存大小按16倍数分配,所以往上取16的倍数,就是64,也就是图中深绿色部分
左2,去掉调试模式下字节数,正好8 + (4*2) = 16,16倍数,不需要padding部分
cookie字节作用
调试模式下Complex大小为64字节,换算成16进制是40。然后,最后一位bit来表示是分配内存还是回收内存。对程序来说,此时是获得了一块新内存,所以是1。加上之前的40,也就是41。所以cookie字节内容为00000041
动态分配数组内存大小
Complex[3]大小:
- 3个Complex:8*3
- 调试模式:32+4
- cookie:4*2
- 多一个整数记录数组的个数,图中白色部分;整数大小为4
为何需要delete[]
String* p = new String[3];
...
delete[] p; //唤起3次dtor
String* p = new String[3];
...
delete p; //唤起1次dtor
不管是delete还是delete[],都不影响下面的整块内存删除。因为这一整块的大小由cookie记录的,只要cookie正确,这一块内存就能正确删除,不会内存泄漏。
但只有写delete[],编译器才知道要删除的是数组,所以才会去调用3次析构函数,不然就只会执行一次析构函数,回收掉这一个object里的动态内存。但其余的两个object,它们中的动态内存就会一直留着,变成内存泄露
这里也可以看出来,如果class里面没有指针,这里使用 delete 而不是 delete[] 也不会造成内存泄漏。因为根本不用调用析构函数来处理动态内存(当然这是不推荐的!按照规范写!)