有了
malloc/free
為什麼還要 ?
malloc與
free
是c++/c
語言的標準庫函式,
new/delete
是c++
對於非內部資料型別的物件而言,光用
maloc/free
無法滿足動態物件的要求。物件在建立的同時要自動執行建構函式,物件在消亡之前要自動執行析構函式。
由於 malloc/free
是庫函式而不是運算 符,不在編譯器控制許可權之內,不能夠把執行建構函式和析構函式的任務強加於
malloc/free。
因此c++
語言需要乙個能完成動態記憶體分配和初 始化工作的運算子
new,以及乙個能完成清理與釋放記憶體工作的運算子
delete
。注意new/delete
不是庫函式。
我們先看一看
malloc/free
和new/delete
如何實現物件的動態記憶體管理,見示例
7-8。
class obj 「
destroy
」<< endl; }
「initialization
」<< endl; }
「destroy
」<< endl; }
}; void usemallocfree(void)
void usenewdelete(void)
示例用malloc/free
和new/delete
如何實現物件的動態記憶體管理
類obj的函式
initialize
模擬了建構函式的功能,函式
destroy
模擬了析構函式的功能。函式
usemallocfree
中,由於
malloc/free
不能執行建構函式與析構函式,必須呼叫成員函式
initialize
和destroy
來完成初始化與清除工作。函 數
usenewdelete
則簡單得多。
所以我們不要企圖用
malloc/free
來完成動態物件的記憶體管理,應該用
new/delete
。由於內部資料型別的「物件」沒有構造與析構的過程,對它們而言
malloc/free
和new/delete
是等價的。
既然new/delete 的功能完全覆蓋了malloc/free ,為什麼c++ 不把malloc/free 淘汰出局呢?這是因為c++ 程式經常要呼叫c 函 數,而c 程式只能用malloc/free 管 理動態記憶體。
如果用free 釋放「new 建立的動態物件」,那麼該物件因無 法執行析構函式而可能導致程式出錯。如果用delete 釋放「malloc 申請的動態記憶體」,理論上講程式不會出錯,但是該程式的可讀性很差。所以new/delete 必須配對使用,malloc/free 也 一樣。
記憶體耗盡怎麼辦?
如果在申請動態記憶體時找不到足夠大的記憶體塊,
malloc
和new
將返回null
指標,宣告記憶體申請 失敗。 通常有三種方式處理「記憶體耗盡」問題。 (
1)判斷指標是否為
null
,如果是則馬上用
return
語句終止本函式。 例 如:
void func(void)
… }
(2)判斷指標是否為
null
,如果是則馬上用
exit(1)
終止整個程式的執行。 例如:
void func(void)
… }
(3)為
new和malloc
設定異常處理函式。例如
visual c++
可以用_set_new_hander
函式為new
設定使用者自己定義的異常處理函式,也可以讓
malloc
享用與new
相同的異常處理函式。詳細內容請參考
c++使用手冊。
上述(1)(
2)方式使用最普遍。如果乙個函式內有多處需 要申請動態記憶體,那麼方式(
1)就顯得力不從心(釋放記憶體很麻煩),應該用方式(
2)來處理。
很多人不忍心用
exit(1)
,問:「不編寫出錯處理程式, 讓作業系統自己解決行不行?」
不行。如果發生「記憶體耗盡」這 樣的事情,一般說來應用程式已經無藥可救。如果不用
把壞程式殺死,它可能會害死作業系統。道理如同:如果不把歹徒擊斃,歹徒在老死之前會犯下更多的罪。
有乙個很重要的現象要告訴大家。對於
32位以上的應用程式而言,無論怎樣使用
malloc
與new
,幾乎不可能導致 「記憶體耗盡」。 我在
windows 98
下用visual c++
編寫了測試程式,見示例
7-9。這個程式會無休止地執行下去,根本不會終止。因為
32位作業系統支援「虛存」,記憶體用完了,自 動用硬碟空間頂替。我只聽到硬碟嘎吱嘎吱地響,
window 98
已經累得對鍵盤、滑鼠毫無反應。
我可以得出這麼乙個結論:對於
32位以上的應用程式,「記憶體耗盡」錯誤處 理程式毫無用處。這下可把
unix
和windows
程式設計師們樂壞了:反正錯誤處理程式不起作用,我就不寫了,省了很多麻煩。
void main(void)
} 示例7-9
試圖耗盡作業系統的記憶體
的使用要點
函式malloc 的原型如下:
void * malloc(size_t size);
用malloc 申請一塊長度為length 的整數型別的記憶體,程式如下:
int *p = (int *) malloc(sizeof(int) * length);
我們應當把注意力集中在兩個要素上:「型別轉換」和「sizeof 」。
malloc
轉 換成所需要的指標型別。
malloc
函式本身並不識別要申 請的記憶體是什麼型別,它只關心記憶體的總位元組數。 我們通常記不住
int, float
等資料型別的變數的確切位元組數。例如
int變數在
16位系統下是
2個位元組,在
32位下是
4個位元組;而
float
變數在16
位系統下是
4個位元組,在
32位下也是
4個位元組。
函 數free
的原型 如下:
void free( void * memblock );
為什麼free 函式不象malloc 函式那樣複雜呢?這是因為指標p 的型別以及 它所指的記憶體的容量事先都是知道的,語句free(p) 能正確地釋放記憶體。如果p 是null 指標,那麼free 對p 無論操作多少次都不會出問題。如果p 不是null 指標,那麼free 對p 連續操作兩次就會導致程式執行錯誤。
的使用要點
運算子new使用起來要比函式
malloc
簡單得多,例如:
int *p1 = (int *)malloc(sizeof(int) * length);
int *p2 = new int[length];
這是因為
new內建了
sizeof
、型別轉換和型別安全檢查功能。對於非內部資料型別的物件而言,
new在建立動態物件的同時完成了初始化工 作。如果物件有多個建構函式,那麼
new 的語句也可以有多種形 式。 例如:
class obj
void test(void)
如果用new 建立物件陣列,那麼只能使用物件的無引數建構函式。 例如
建立100 個動態物件
不能寫成
建立100 個動態物件的同時賦初值1
在用delete 釋 放物件陣列時,留意不要丟了符號『 』。 例如
正確的用法
錯誤的用法
後者相當於delete objects[0] ,漏掉了另外99 個物件。
一些心得體會
我認識不少技術不錯的
c++/c
程式設計師,很少有人能拍拍胸脯說通曉 指標與記憶體管理(包括我自己)。我最初學習
c語言時特別怕指標,導致我開發第乙個應用軟體(約1萬行
c**)時沒有使用乙個指標,全用陣列來頂替 指標,實在蠢笨得過分。躲避指標不是辦法,後來我改寫了這個軟體,**量縮小到原先的一半。
我的經驗教訓是: (
1)越是怕指標,就越要使用指標。不會正確使用指標,肯定算不上是合格的程式設計師。 (
2)必須養成「使用偵錯程式逐步跟蹤程式」的習慣,只有這樣才能發現問題的本質。
《 高質量C 程式設計指南 》學習重點十
物件 object 是類 class 的乙個例項 instance 如果將物件比作房子,那麼類就是房子的設計圖紙。所以物件導向設計的重點是類的設計,而不是物件的設計。對於 c 程式而言,設計孤立的類是比較容易的,難的是正確設計基類及其派生類。本章僅僅論述 繼承 inheritance 和 組合 co...
高質量C 程式設計指南 》學習重點四
7 章 記憶體管理 記憶體分配方式有三種 從靜態儲存區域分配。內 存在程式編譯的時候就已經分配好,這塊內存在程式的整個執行期間都存在。例 如全域性變數,static 變數。在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單 元自動被釋放。棧記憶體分配運算內...
《 高質量C 程式設計指南 》學習重點十
物件 object 是類 class 的乙個例項 instance 如果將物件比作房子,那麼類就是房子的設計圖紙。所以物件導向設計的重點是類的設計,而不是對 象的設計。對於c 程式而言,設計孤立的類是比較容易的,難的是正確設計基類及其派生類。本章僅僅論述 繼承 inheritance 和 組合 co...