首先我们来看一下operator new,因为new中调用了operator new,下面是operator new 的部分源码
typedef void (*)() new_handler;
void *operator new(size_t n) throw(bad_alloc) {
// corner cases...
void *ptr = 0;
new_handler nHandler = nullptr;
while (true) {
if (ptr = malloc(n)) {
return ptr;
} else {
if (nHandler = get_new_handler()) {
(*nHandler)();
} else {
// throw exception...
}
}
}
// ...
}
首先我们来看一下实现思路,有一个空指针ptr在循环中使用malloc开辟空间,malloc开辟不成功会返回空指针NULL,其实也就是0,申请不成功的话会去尝试扩容内存,nHandler是用来扩容,具体怎么实现,我们本篇博客为初阶探讨,不进行深入讨论,如果扩容失败,那么就会抛出异常,所以我们可以把operator new 看成 malloc的升级版,malloc只会申请失败后返回NULL,不会去做其他处理
下面我们看一下new的部分反汇编,因为我没有在VS中找到new的源码,gcc中应该会有
A* ptr1 = new A;
00736EC4 push 4
00736EC6 call operator new (0731154h)
00736ECB add esp,4
00736ECE mov dword ptr [ebp-104h],eax
00736ED4 mov dword ptr [ebp-4],0
00736EDB cmp dword ptr [ebp-104h],0
00736EE2 je __$EncStackInitStart+5Dh (0736EF7h)
00736EE4 mov ecx,dword ptr [ebp-104h]
00736EEA call A::A (07314B0h)
....
可以看到,它压栈后首先调用了operator new,中间经过处理后(初阶,不做深入讨论),调用了A的默认构造函数,所以这就是new和opeator new的区别所在,多调用了类的构造函数
operator new 有几个重载,因为本篇博客不是主要写opeator new的,就不多介绍,或许之后会单开一篇博客去写operator new
delete 里面呢,调用了operator delete,同样,operator delete也有几个重载一一与opeator new 相对应,operator 里面呢调用了free,看一下delete汇编代码
delete ptr1;//自定义对象
00782A0E mov eax,dword ptr [ptr1]
00782A11 mov dword ptr [ebp-110h],eax
00782A17 cmp dword ptr [ebp-110h],0
00782A1E je __$EncStackInitStart+0DBh (0782A35h)
00782A20 push 1
00782A22 mov ecx,dword ptr [ebp-110h]
00782A28 call A::`scalar deleting destructor' (078137Ah)
00782A2D mov dword ptr [ebp-118h],eax
00782A33 jmp __$EncStackInitStart+0E5h (0782A3Fh)
00782A35 mov dword ptr [ebp-118h],0
delete ptr1;//内置类型 int
00A71B12 mov eax,dword ptr [ptr1]
00A71B15 mov dword ptr [ebp-0ECh],eax
00A71B1B push 4
00A71B1D mov ecx,dword ptr [ebp-0ECh]
00A71B23 push ecx
00A71B24 call operator delete (0A710A0h)
00A71B29 add esp,8
00A71B2C cmp dword ptr [ebp-0ECh],0
00A71B33 jne __$EncStackInitStart+0A5h (0A71B41h)
00A71B35 mov dword ptr [ebp-0F4h],0
00A71B3F jmp __$EncStackInitStart+0B5h (0A71B51h)
00A71B41 mov dword ptr [ptr1],8123h
00A71B48 mov edx,dword ptr [ptr1]
00A71B4B mov dword ptr [ebp-0F4h],edx
这是两段完整的delete 汇编代码,可以看到他释放对象时只调用了对象的析构函数,并没有调用free or operator delete,当然也可能是编译器不同,我的测试环境为VS2022,理论上来讲他应该是像内置类型一样,先析构再调用operator delete 然后operator delete内部 再调用free,或许是编译器做了什么优化导致他并没有这么做
总结:new内部调用了operator new,operator new 调用了 malloc,并在申请内存失败时可以抛出异常,malloc仅返回NULL,new在operator之上升级了可以调用对象的构造函数,operator delete调用了free,它俩其实很相似,没什么不同,delete 在释放对象时调用析构函数,在释放内置类型指针时调用operator delete