記憶體管理的基礎是要知道怎麼獲得以及釋放記憶體,如你所知,在C/C++中就是調用new和delete操作。
1. 分清operator new和new operator
全域函數operator new通常這樣聲明:
void * operator new(size_t size);
傳回值類型是void*,表示其返回的是一個未經處理(raw)的指標,指向未初始化的記憶體。參數size_t確定分配多少記憶體。你能增加額外的參數重載函數operator new,但是第一個參數類型必須是size_t。標頭檔<new>中有一個很好的重載的例子,那就是placement new,它看上去象這樣:
void * operator new(size_t, void *location)
{
return location;
}
這初看上去有些陌生,但它卻是new操作符的一種常見重載方法,使用一個額外的變數buffer,當new操作符隱含調用operator new函數時,把這個變數傳遞給它。被調用的operator new函數除了持有強制的參數size_t外,還必須接受void*指標參數,指向構造對象佔用的記憶體空間。未被使用的(但是強制的)參數size_t沒有參數名字,以防止編譯器警告說它未被使用。在使用placement new的情況下,調用者已經獲得了指向記憶體的指標,因為調用者知道對象應該放在哪裡。placement new需要做的就是返回傳遞給它的指標。
我們更經常使用的new是new操作符(new operator),而非操作符new(operator new),如當你使用new操作符構建一個對象的時候,實際上做了兩件事情,一是調用operator new函數擷取記憶體,二是調用對象的建構函式,如:
string *ps = new string("Hello, world!");
它完成與下面代碼相似的功能:
void *memory = operator new(sizeof(string)); // 為String對象得到未經處理的記憶體
call string::string("Hello, world!") on *memory; // 調用建構函式初始化記憶體中的對象
string *ps = static_cast<string*>(memory); // ps指標指向新的對象
注意第二步中建構函式的調用只能由編譯器完成,使用者是不允許這樣操作的,也就是說如果你想建立一個堆對象就必須用new操作符,不能直接像上面一樣調用建構函式來初始化堆對象。
new操作符(new operator)是編譯器內建的,其行為被語言固定下來,不受使用者控制。但是它們所調用的記憶體配置函數也就是操作符new(operator new)則可以根據需要進行重載。試著回顧new操作符(new operator)與操作符new(operator new)的關係,如果你想在堆上建立一個對象,應該用new操作符。它既分配記憶體又為對象調用建構函式。如果你僅僅想分配記憶體,就應該調用operator new函數,它不會調用建構函式。如果你想定製自己專屬的記憶體配置過程,你應該重載全域的operator new函數,然後使用new操作符,new操作符會調用你定製的operator new。如果你想在一塊已經獲得指標的記憶體裡建立一個對象,應該使用placement new。
最後需要記住的一點是,delete和new一樣具有以上的特性,只是需要注意的一點是delte操作符中是首先調用對象的解構函式,然後再調用operator delete函數的。
2. 針對數組的new[]和delete[]操作
建立數組時new操作符(new[])的行為與單個對象建立(new)有少許不同:
第一是記憶體不再調用用operator new函數進行分配,代替以operator new[]函數(常稱作array new)。它與operator new一樣能被重載,允許定製數組的記憶體配置,就象定製單個對象記憶體配置一樣。
第二個不同是new[]操作時調用建構函式的數量。對於new[]而言,在數組裡的每一個對象的建構函式都必須被調用。
delete[]操作符的語義基本上和new[]相同,他們的實作類別似這樣:
void * operator new[](size_t size)
{
cout << "new size of array in new[](): " << size << endl;
int *g =(int *) malloc(sizeof(size));
return g;
}
void operator delete[](void* p)
{
cout << "delete address of array pointer in delete[](): " << p << endl;
free(p);
}
3. operator new和delete函數的實現
operator new實際上總是以標準的C malloc()完成,雖然並沒有規定非得這麼做不可。同樣,operator delete也總是以標準得C free()來實現,不考慮異常處理的話他們類似下面的樣子:
extern void* operator new( size_t size )
{
if( size == 0 )
size = 1; // 這裡保證像 new T[0] 這樣得語句也是可行的
void *last_alloc;
while( !(last_alloc = malloc( size )) )
{
if( _new_handler )
( *_new_handler )();
else
return 0;
}
return last_alloc;
}
extern void operator delete( void *ptr )
{
if(ptr) // 從這裡可以看出,刪除一個null 指標是安全的
free( (char*)ptr );
}