三国演义里面说过一句话:天下大事,合久必分,分久必合。有相聚,就有分离的时候。今天咱们主要聊聊operator delete的故事ios
今天咱们主要学习知识点:数组
1.delete的调用流程。 2.咱们重载了delete以后能干啥。 3.placement delete有啥用。函数
测试代码以下:学习
/**************************************************************************** ** ** Copyright (C) 2019 635672377@qq.com ** All rights reserved. ** ****************************************************************************/
/* 测试对象的new、delete,在VS2017更容易观察 */
#ifndef obj_new_delete_h
#define obj_new_delete_h
#include <new>
#include <memory>
#include <iostream>
using std::cout;
using std::endl;
namespace obj_new_delete
{
class Obj {
public:
Obj():mCount(0) { cout << "Obj ctor" << endl; }
~Obj() { cout << "~Obj dtor" << endl; }
private:
int mCount;
};
void test_new_obj() {
Obj *obj = new Obj();
delete obj;
}
}
#endif // obj_new_delete_h
复制代码
老规矩,咱们转到反汇编的代码:测试
00287340 mov eax,dword ptr [obj]
; delete obj
00287343 mov dword ptr [ebp-104h],eax
00287349 mov ecx,dword ptr [ebp-104h]
0028734F mov dword ptr [ebp-0F8h],ecx
; 若是ecx为0,不用调用operator delete和析构函数
00287355 cmp dword ptr [ebp-0F8h],0
0028735C je obj_new_delete::test_delete_obj+0D3h (0287373h)
0028735E push 1
00287360 mov ecx,dword ptr [ebp-0F8h]
; 析构代理函数
00287366 call obj_new_delete::Obj::`scalar deleting destructor' (0281212h)
0028736B mov dword ptr [ebp-10Ch],eax
00287371 jmp obj_new_delete::test_delete_obj+0DDh (028737Dh)
00287373 mov dword ptr [ebp-10Ch],0
obj_new_delete::Obj::`scalar deleting destructor':
00281212 jmp obj_new_delete::Obj::`scalar deleting destructor' (0282820h)
00282840 mov dword ptr [this],ecx
00282843 mov ecx,dword ptr [this]
; 调用对象的析构函数
00282846 call obj_new_delete::Obj::~Obj (02814C4h)
0028284B mov eax,dword ptr [ebp+8]
; 须要释放内存
0028284E and eax,1
00282851 je obj_new_delete::Obj::`scalar deleting destructor'+41h (0282861h)
00282853 push 4
00282855 mov eax,dword ptr [this]
; 传递对象的首地址放到eax寄存器中
00282858 push eax
; 调用局部的operator delete,第一参数为首地址,第二个参数为对象的大小
00282859 call operator delete (0281325h)
0028285E add esp,8
00282861 mov eax,dword ptr [this]
operator delete:
00281325 jmp operator delete (02832E0h)
; 调用全局的operator delete,只有一个参数为首地址
operator delete:
002811B3 jmp operator delete (02840C0h)
复制代码
这里我顺便把operator delete的源码贴出来ui
// 局部operator delete源码
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block, size_t const) noexcept {
operator delete(block);
}
// 全局operator delete源码
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block) noexcept {
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK);
#else
free(block);
#endif
}
复制代码
根据这汇编代码我画出流程图:this
看到了这张流程图,你必定对图中○1,○2有几点有疑问。spa
1.什么析构代理函数?scala
从名字中就能够看出就是个代理函数,它从中不只调用咱们本身写的析构函数,还作点其余幕后事情,好比流程图中的是否须要释放内存3d
2.为何在流程图中还有个是否须要释放内存的判断?
你们请看这段代码,是否会释放内存。
void test_delete_obj() {
Obj *obj = new Obj();
obj->~Obj();
}
复制代码
若是咱们本身手动调用了析构函数,这时系统是不会帮咱们释放内存的。
我把上面的代码反汇编看下:
; obj->~Obj()
00EF7340 push 0
00EF7342 mov ecx,dword ptr [obj]
00EF7345 call obj_new_delete::Obj::`scalar deleting destructor' (0EF1212h)
复制代码
能够看到这里首先push 0做为一个参数,push 0就表示仅仅调用析构函数,并不会释放内存。仔细看operator delete反汇编代码,这里push 1做为参数,表示须要释放内存,我在上面也作了注释。
由于C++运行时系统在delete就已经判断了,若是指针为空则不会调用delete。
最后咱们须要注意下:
- 咱们代码调用的析构函数,其实不是咱们本身写的析构函数,而是编译器写的析构代理函数。
- 析构代理函数里面又作了其余的事,好比是否须要释放内存,再好比对象数组又是怎么释放内存。
只要咱们重载了operator new,就应该对应的重载operator delete,他们两个是一一对应的东西。具体怎么重载的,在《咱们来new个对象》中已经贴出代码了。
与placement new是个对应,前者是为了在原有内存上再次构造对象。后者是为了在异常负责回收内存。
在通常状况下,其实placement delete起不到做用的。只有在异常状况下,才会被C++运行时系统调用,用来释放内存。
具体怎么重载的,就不在多说了,在《咱们来new个对象》中已经贴出代码了。
这个源码在vcruntime_new.h中
inline void __CRTDECL operator delete(void*, void*) noexcept {
return;
}
复制代码
恐怕会让你有点失望,在vs2017的版本中,这个什么里面都没有作的。搞的我也是摸不着头脑。
这节咱们知道了operator delete调用流程,对对象的消失有了更深刻的理解。同时知道了析构代理函数存在以及做用。 placement delete做用也是不容咱们忽视的。