三國演義裏面說過一句話:天下大事,合久必分,分久必合。有相聚,就有分離的時候。今天咱們主要聊聊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做用也是不容咱們忽視的。