讲自由存储区和堆的关系:https://www.cnblogs.com/qg-whz/p/5060894.html
视频链接: https://www.bilibili.com/video/av19038490/?p=8
1. new和delete对象时 编译器的执行过程(new表达式)
在我们 new
一个对象时,编译器大致分三个步骤进行:
- 分配对象尺寸大小的空间
- 将void指针转型为complex指针
- 通过2这根指针去调用构造函数
在 delete
这个对象指针时,编译器分为两个步骤:
- 调用析构函数
- 释放内存
2. 动态分配所得内存的情况
2.1 new对象时
在debug
模式下系统为 一个对象分配的内存情况 是带第一块和第三块。
上和底部红色为cookie(用于记录整块内存大小,方便回收),青色为complex数据大小8字节,灰色为调试模式下编译器多加的部分,最终内存大小必须是16的倍数,绿色为其填补。
在 release
模式下没有灰色部分,内存大小为16,程序员能看到的只是数据的大小8字节,不包含上下coolie大小。最终内存大小也必须是16的倍数。
cookie作用:用于记录整块内存大小,方便回收。其内容记录为size+1(size为此对象所占内存的大小,1表示是分配或回收的状态,16位整数倍所以最后4位都可以用)。
2.2 new数组时 (array new)
new array[3] 内存里会多4个字节用于计数 (counter)数组的长度。
new array[3] 一定要和 delete[] 搭配,否则内存泄漏。delete告诉编译器删除一个指针,delete[]告诉编译器删除一个数组。
3. 重载 operator new; operator delete; operator new; operator new[];
(operator new)
可以用在内存管理,内存池设计。
new complex;
这里的new是expression表达式,分解的三个步骤这个事实不能改变,但是步骤里调用的operator new
可以重载。delete一样。
3.1 重载全局的operator new/delete/[]
重载的是全局的new,只有编译器会去调用它们,可能导致系统崩掉。
3.2 重载member new/delete
3.3 示例(operator new)
如果程序员这么写 Foo* pf = ::new Foo;
::delete pf;
就是会绕过重载的那个new/delete,直接使用全局的new/delete。
4. 重载operator new()、operator delete()
(placement new())
4.1 operator new()
我们可以重载class member operator new()
,写出多个版本,前提是每一版本的声明都必须有独特的参数列,其中第一个参数必须是size_t,其余参数以new所指定的placement argument为初值,出现于new(…)小括号内的便是所谓placement arguments。
如下在构造函数中故意抛出异常。
第五个重载函数operator new()
的第一个参数为long
型,编译报错。
4.2 operator delete()
我们也可以重载class member operator delete(),写出多个版本。但他们绝对不会被 delete
调用。只有当 new 所调用的ctor抛出exception,才会调用这些重载版本的operator delete()
。它只可能这样被调用,主要用来归还未能完成创建成功的object所占用的memory。
构造一个对象时:在调用其中一个operator new()
分配完空间后, 调用构造函数时出现异常,在构造这个对象过程中出错,但是此时空间已经分配出来了,所以释放掉这个内存,否则内存泄漏。(operator delete()是,在构造对象出错后,给一个机会去释放内存)
如下在构造函数中故意抛出异常。
如下构造p5这个对象时不会调用默认构造,会调用自己写的那个 专门抛出异常的构造函数。然后编译器调用对应operator delete()
,但是如果没有写operator delete()
编译器也不会去care,毕竟new的内存回收由程序员决定。
4.3 示例(placement new)
标准库的例子string,其中Rep用作reference counter。