大家好,我是苏貝,本篇博客带大家了解C++的operator new/delete,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- 1 new/delete的底层
- 2 new/delete的底层调用顺序
- 3 delete[ ]调用析构函数的次数
- (A) 方法1
- (B) 方法2
- 4 new/new 类型[ ]必须和delete/delete[ ]匹配
注意:operator new/delete了解就可以
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数(不是对new和delete的运算符重载),new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
1 new/delete的底层
operator new是对malloc的封装,operator delete是对free的封装。
如果想使用operator new,那么必须将它写完整,不能简写成new
运算符在编译后就没有了,编译后的指令里没有运算符。所以new/delete在编译后就没有了,变成了下图所示的。new的底层是operator new,operator new的底层是malloc。delete的底层是operator delete,operator delete的底层是free
题外话:问,sizeof和strlen都是在什么时候得出结果的?
sizeof:运算符,在编译时就根据内置类型大小的定义或自定义类型根据结构体对齐规则计算出了结果
strlen:函数,在程序运行时才得出结果
2 new/delete的底层调用顺序
new毫无疑问是先operator new(先动态开辟空间)再调用构造函数,那么delete是先operator delete还是先调用析构函数呢?
因此,delete是先调用析构函数,再operator delete
3 delete[ ]调用析构函数的次数
问:下图中delete[ ] p2;语句会调用几次析构函数?
10次,即new的对象的个数。
为什么编译器知道是new的对象个数10?
因为如果new的类型是自定义类型,且个数>1,且编译器会调用析构函数,那么编译器会在动态开辟的空间前再开辟4个字节的空间去存储new自定义类型的个数。这个4字节的空间就是在delete[ ]时,让编译器知道要调用多少次析构函数。
注意:即使会在动态开辟的空间前再开辟4个字节的空间去存储new自定义类型的个数,但是p2指向的还是b而非a
下面来证明
(A) 方法1
进入调试,显示出内存
在搜索框中输入p2后按回车键,就会显现出p2指向的空间的值。往上翻即可看见p2指向的空间前4个字节的空间的值是0a(十六进制)即10。成功证明
将new的对象改为5个,看看是否还是在开辟的空间前开辟了一个4字节的空间存储对象个数5。
事实确实如此
如果我们没有显示定义析构函数,那么编译器会自动生成一个析构函数。
问:如果编译器认为类A不需要在生命周期结束时调用析构函数,那么还会不会在开辟的空间前开辟一个4字节的空间存储对象个数?
(下图较之前只是删除了析构函数。如果我们显示定义了析构函数,那么编译器在对象生命周期结束后一定会调用析构函数)
答案是不会。如果编译器认为类A不需要在生命周期结束时调用析构函数,那么就不会在开辟的空间前开辟一个4字节的空间存储对象个数
如果new的是内置类型,那么会不会在开辟的空间前开辟一个4字节的空间存储对象个数?
不会,因为内置类型没有析构函数,所以不会
(B) 方法2
看push后面的数据14(十六进制)即16+4=20字节。sizeof(A)==4,4*5=20,所以就不会在开辟的空间前开辟一个4字节的空间存储对象个数
再看一个会在开辟的空间前开辟一个4字节的空间存储对象个数的例子:在类A中显示写析构函数
看push后面的数据18(十六进制)即16+8=24字节。sizeof(A)==4,4*5=20,所以会在开辟的空间前开辟一个4字节的空间存储对象个数
4 new/new 类型[ ]必须和delete/delete[ ]匹配
如果不匹配使用,如new 类型[ ]却用delete来释放空间,会报错
为什么?
因为如果new的是自定义类型,且new的对象>1,且编译器会调用析构函数,那么会在开辟的空间前开辟一个4字节的空间存储对象个数。
现在delete p,即从p的位置开始释放,那么就落下了存储对象个数的空间,释放开辟的空间能从之间释放吗?不能,只能从最开始即a点释放,所以报错
因此,如果new的是自定义类型,且new的对象>1,但编译器不会调用析构函数,那么就不会报错,因为不会在开辟的空间前开辟一个4字节的空间存储对象个数。
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️