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 做略微修改
