1、new
new操作針對數據類型的處理,分為兩種情況:
(1)簡單數據類型(包括基本數據類型和不需要構造函數的類型)
代碼實例:
int* p = new int;
匯編碼如下:
int* p = new int; 00E54C44 push 4 00E54C46 call operator new (0E51384h) 00E54C4B add esp,4
分析:傳入4byte的參數后調用operator new。其源碼如下:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // report no memory _THROW_NCEE(_XSTD bad_alloc, ); } return (p); }
分析:調用malloc失敗后會調用_callnewh。如果_callnewh返回0則拋出bac_alloc異常,返回非零則繼續分配內存。
這個_callnewh是什么呢?它是一個new handler,通俗來講就是new失敗的時候調用的回調函數。可以通過_set_new_handler來設置。下面舉個實例:
#include <stdio.h> #include <new.h> int MyNewHandler(size_t size) { printf("Allocation failed.Try again"); return 1; //continue to allocate //return 0; //stop allocating,throw bad_alloc } void main() { // Set the failure handler for new to be MyNewHandler. _set_new_handler(MyNewHandler); while (1) { int* p = new int[10000000]; } }
在new基本數據類型的時候還可以指定初始化值,比如:
int* p = new int(4);
總結:
- 簡單類型直接調用operator new分配內存;
- 可以通過new_handler來處理new失敗的情況;
- new分配失敗的時候不像malloc那樣返回NULL,它直接拋出異常。要判斷是否分配成功應該用異常捕獲的機制;
(2)復雜數據類型(需要由構造函數初始化對象)
代碼實例:
class Object { public: Object() { _val = 1; } ~Object() { } private: int _val; }; void main() { Object* p = new Object(); }
匯編碼如下:
Object* p = new Object(); 00AD7EDD push 4 00AD7EDF call operator new (0AD1384h) 00AD7EE4 add esp,4 00AD7EE7 mov dword ptr [ebp-0E0h],eax 00AD7EED mov dword ptr [ebp-4],0 00AD7EF4 cmp dword ptr [ebp-0E0h],0 00AD7EFB je main+70h (0AD7F10h) 00AD7EFD mov ecx,dword ptr [ebp-0E0h] 00AD7F03 call Object::Object (0AD1433h) //在new的地址上調用構造函數 00AD7F08 mov dword ptr [ebp-0F4h],eax 00AD7F0E jmp main+7Ah (0AD7F1Ah) 00AD7F10 mov dword ptr [ebp-0F4h],0 00AD7F1A mov eax,dword ptr [ebp-0F4h] 00AD7F20 mov dword ptr [ebp-0ECh],eax 00AD7F26 mov dword ptr [ebp-4],0FFFFFFFFh 00AD7F2D mov ecx,dword ptr [ebp-0ECh] 00AD7F33 mov dword ptr [p],ecx
總結:
new 復雜數據類型的時候先調用operator new,然后在分配的內存上調用構造函數。
2、delete
delete也分為兩種情況:
(1)簡單數據類型(包括基本數據類型和不需要析構函數的類型)。
int *p = new int(1); delete p;
delete的匯編碼如下:
delete p; 00275314 mov eax,dword ptr [p] 00275317 mov dword ptr [ebp-0D4h],eax 0027531D mov ecx,dword ptr [ebp-0D4h] 00275323 push ecx 00275324 call operator delete (0271127h)
分析:傳入參數p之后調用operator delete,其源碼如下:
void operator delete( void * p ) { RTCCALLBACK(_RTC_Free_hook, (p, 0)); free( p ); }
RTCCALLBACK默認是空的宏定義,所以這個函數默認情況下就是簡單的調用free函數。
總結:
delete簡單數據類型默認只是調用free函數。
(2)復雜數據類型(需要由析構函數銷毀對象)
代碼實例:
class Object { public: Object() { _val = 1; } ~Object() { cout << "destroy object" << endl; } private: int _val; }; void main() { Object* p = new Object; delete p; }
部分匯編碼如下:
012241F0 mov dword ptr [this],ecx 012241F3 mov ecx,dword ptr [this] 012241F6 call Object::~Object (0122111Dh) //先調用析構函數 012241FB mov eax,dword ptr [ebp+8] 012241FE and eax,1 01224201 je Object::`scalar deleting destructor'+3Fh (0122420Fh) 01224203 mov eax,dword ptr [this] 01224206 push eax 01224207 call operator delete (01221145h) 0122420C add esp,4
總結:
delete復雜數據類型先調用析構函數再調用operator delete。
3、new 數組
new[]也分為兩種情況:
(1)簡單數據類型(包括基本數據類型和不需要析構函數的類型)。
new[] 調用的是operator new[],計算出數組總大小之后調用operator new。
值得一提的是,可以通過()初始化數組為零值,實例:
char* p = new char[32]();
等同於:
char *p = new char[32]; memset(p, 32, 0);
總結:
針對簡單類型,new[]計算好大小后調用operator new。
(2)復雜數據類型(需要由析構函數銷毀對象)
實例:
class Object { public: Object() { _val = 1; } ~Object() { cout << "destroy object" << endl; } private: int _val; }; void main() { Object* p = new Object[3]; }
new[]先調用operator new[]分配內存,然后在p的前四個字節寫入數組大小,最后調用三次構造函數。
實際分配的內存塊如下:

這里為什么要寫入數組大小呢?因為對象析構時不得不用這個值,舉個例子:
class Object { public: Object() { _val = 1; } virtual ~Object() { cout << "destroy Object" << endl; } private: int _val; }; class MyObject : public Object { public: ~MyObject() { cout << "destroy MyObject" << endl; } private: int _foo; }; void main() { Object* p = new MyObject[3]; delete[] p; }
釋放內存之前會調用每個對象的析構函數。但是編譯器並不知道p實際所指對象的大小。如果沒有儲存數組大小,編譯器如何知道該把p所指的內存分為幾次來調用析構函數呢?
總結:
針對復雜類型,new[]會額外存儲數組大小。
4、delete 數組
delete[]也分為兩種情況:
(1)簡單數據類型(包括基本數據類型和不需要析構函數的類型)。
delete和delete[]效果一樣
比如下面的代碼:
int* pint = new int[32]; delete pint; char* pch = new char[32]; delete pch;
運行后不會有什么問題,內存也能完成的被釋放。看下匯編碼就知道operator delete[]就是簡單的調用operator delete。
總結:
針對簡單類型,delete和delete[]等同。
(2)復雜數據類型(需要由析構函數銷毀對象)
釋放內存之前會先調用每個對象的析構函數。
new[]分配的內存只能由delete[]釋放。如果由delete釋放會崩潰,為什么會崩潰呢?
假設指針p指向new[]分配的內存。因為要4字節存儲數組大小,實際分配的內存地址為[p-4],系統記錄的也是這個地址。delete[]實際釋放的就是p-4指向的內存。而delete會直接釋放p指向的內存,這個內存根本沒有被系統記錄,所以會崩潰(delete指針還是delete指針指向的內存空間)。
總結:
針對復雜類型,new[]出來的內存只能由delete[]釋放。
【總結】:
1、new
new操作針對數據類型的處理,分為兩種情況:
(1) 簡單數據類型(包括基本數據類型和不需要構造函數的類型)
- 簡單類型直接調用operator new分配內存;
- 可以通過new_handler來處理new失敗的情況;
- new分配失敗的時候不像malloc那樣返回NULL,它直接拋出異常。要判斷是否分配成功應該用異常捕獲的機制;
(2)復雜數據類型(需要由構造函數初始化對象)
new 復雜數據類型的時候先調用operator new,然后在分配的內存上調用構造函數。
2、delete
delete也分為兩種情況:
(1) 簡單數據類型(包括基本數據類型和不需要析構函數的類型)
delete簡單數據類型默認只是調用free函數。
(2)復雜數據類型(需要由析構函數銷毀對象)
delete復雜數據類型先調用析構函數再調用operator delete。
3、new 數組
new[]也分為兩種情況:
(1) 簡單數據類型(包括基本數據類型和不需要析構函數的類型)
針對簡單類型,new[]計算好大小后調用operator new。
(2)復雜數據類型(需要由析構函數銷毀對象)
針對復雜類型,new[]會額外存儲數組大小。
4、delete 數組
delete[]也分為兩種情況:
(1) 簡單數據類型(包括基本數據類型和不需要析構函數的類型)
針對簡單類型,delete和delete[]等同。
(2)復雜數據類型(需要由析構函數銷毀對象)
針對復雜類型,new[]出來的內存只能由delete[]釋放。
轉自:https://blog.csdn.net/passion_wu128/article/details/38966581 做略微修改