C++ new/delete、malloc/free

http://www.cnblogs.com/growup/archive/2011/06/27/2091101.htmlhtml

http://blog.csdn.net/passion_wu128/article/details/38966581web

new和delete最終調用malloc和free數組

1.malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們均可用於申請動態內存和釋放內存安全

2.對於非內部數據類型的對象而言,光用maloc/free沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡以前要自動執行析構函數。由malloc/free是庫函數而不是運算符,不在編譯器控制權限以內,不可以把執行構造函數和析構函數的任務強加於malloc/free。函數

3.所以C++語言須要一個能完成動態內存分配和初始化工做的運算符new,以一個能完成清理與釋放內存工做的運算符delete。注意new/delete不是庫函數。 
4.C++程序常常要調用C函數,而C程序只能用malloc/free管理動態內存。 
5.new能夠認爲是malloc加構造函數的執行。new出來的指針是直接帶類型信息的。而malloc返回的都是void*指針。this

new delete在實現上其實調用了malloc,free函數編碼

6.new創建的對象你能夠把它當成一個普通的對象,用成員函數訪問,不要直接訪問它的地址空間;malloc分配的是一塊內存區域,就用指針訪問好了,並且還能夠在裏面移動指針.spa

7.new 創建的是一個對象;alloc分配的是一塊內存..net

***************************************scala

相同點:均可用於申請動態內存和釋放內存

不一樣點 
(1)操做對象有所不一樣 
malloc與free是C++/C 語言的標準庫函數,new/delete 是C++的運算符。對於非內部數據類的對象而言,光用maloc/free 沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數, 對象消亡以前要自動執行析構函數。因爲malloc/free 是庫函數而不是運算符,不在編譯器控制權限以內,不可以把執行構造函數和析構函數的任務強加malloc/free。

(2)在用法上也有所不一樣 
函數malloc 的原型以下: 
void * malloc(size_t size); 
用malloc 申請一塊長度爲length 的整數類型的內存,程序以下: 
int *p = (int *) malloc(sizeof(int) * length); 
咱們應當把注意力集中在兩個要素上:「類型轉換」和「sizeof」。 
malloc 返回值的類型是void *,因此在調用malloc 時要顯式地進行類型轉換,將void * 轉換成所須要的指針類型。 
malloc 函數自己並不識別要申請的內存是什麼類型,它只關心內存的總字節數。

函數free 的原型以下: 
void free( void * memblock ); 
爲何free 函數不象malloc 函數那樣複雜呢?這是由於指針p 的類型以及它所指的內存的容量事先都是知道的,語句free(p)能正確地釋放內存。若是p 是NULL 指針,那麼free

對p 不管操做多少次都不會出問題。若是p 不是NULL 指針,那麼free 對p連續操做兩次就會致使程序運行錯誤。

new/delete 的使用要點 
運算符new 使用起來要比函數malloc 簡單得多,例如: 
int *p1 = (int *)malloc(sizeof(int) * length); 
int *p2 = new int[length]; 
這是由於new 內置了sizeof、類型轉換和類型安全檢查功能。對於非內部數據類型的對象而言,new 在建立動態對象的同時完成了初始化工做。若是對象有多個構造函數,那麼new 的語句也能夠有多種形式。

若是用new 建立對象數組,那麼只能使用對象的無參數構造函數。例如 
Obj *objects = new Obj[100]; // 建立100 個動態對象 
不能寫成 
Obj *objects = new Obj[100](1);// 建立100 個動態對象的同時賦初值1 
在用delete 釋放對象數組時,留意不要丟了符號‘[]’。例如 
delete []objects; // 正確的用法 
delete objects; // 錯誤的用法 
後者至關於delete objects[0],漏掉了另外99 個對象。

***************************************

1  new自動計算須要分配的空間,而malloc須要手工計算字節數 
2  new是類型安全的,而malloc不是,好比: 
int* p = new float[2]; // 編譯時指出錯誤 
int* p = malloc(2*sizeof(float)); // 編譯時沒法指出錯誤 
new operator 由兩步構成,分別是 operator new 和 construct 
3  operator new對應於malloc,但operator new能夠重載,能夠自定義內存分配策略,甚至不作內存分配,甚至分配到非內存設備上。而malloc無能爲力 
4  new將調用constructor,而malloc不能;delete將調用destructor,而free不能。 
5  malloc/free要庫文件支持,new/delete則不要。

=============================================================

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;

運行後不會有什麼問題,內存也能完成的被釋放。看下彙編碼就知道operator delete[]就是簡單的調用operator delete。

總結:

針對簡單類型,delete和delete[]等同。


2,複雜數據類型(須要由析構函數銷燬對象)

釋放內存以前會先調用每一個對象的析構函數。

new[]分配的內存只能由delete[]釋放。若是由delete釋放會崩潰,爲何會崩潰呢?

假設指針p指向new[]分配的內存。由於要4字節存儲數組大小,實際分配的內存地址爲[p-4],系統記錄的也是這個地址。delete[]實際釋放的就是p-4指向的內存。而delete會直接釋放p指向的內存,這個內存根本沒有被系統記錄,因此會崩潰。

總結:

針對複雜類型,new[]出來的內存只能由delete[]釋放。

相關文章
相關標籤/搜索