C++new和delete实现原理(汇编解释)
new和delete最終調用malloc和free,關于malloc和free實現原理參見這篇文章:
http://blog.csdn.net/passion_wu128/article/details/38964045
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,然后在分配的內存上調用構造函數。
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。
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[]會額外存儲數組大小。
delete數組
delete[]也分為兩種情況:
1,簡單數據類型(包括基本數據類型和不需要析構函數的類型)。
delete和delete[]效果一樣
比如下面的代碼:
int* pint = new int[32];
delete pint;
?
char* pch = new char[32];
delete pch;
運行后不會有什么問題,內存也能完成的被釋放??聪聟R編碼就知道operator delete[]就是簡單的調用operator delete。
總結:
針對簡單類型,delete和delete[]等同。
2,復雜數據類型(需要由析構函數銷毀對象)
釋放內存之前會先調用每個對象的析構函數。
new[]分配的內存只能由delete[]釋放。如果由delete釋放會崩潰,為什么會崩潰呢?
假設指針p指向new[]分配的內存。因為要4字節存儲數組大小,實際分配的內存地址為[p-4],系統記錄的也是這個地址。delete[]實際釋放的就是p-4指向的內存。而delete會直接釋放p指向的內存,這個內存根本沒有被系統記錄,所以會崩潰。
總結:
針對復雜類型,new[]出來的內存只能由delete[]釋放。
原文:https://blog.csdn.net/passion_wu128/article/details/38966581?
?
總結
以上是生活随笔為你收集整理的C++new和delete实现原理(汇编解释)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 漏桶算法与令牌桶算法
- 下一篇: c++中创建类型测试