高質量C++/C編程指南--第7章內存管理(2)

字號:

7.7 杜絕“野指針”
    “野指針”不是NULL指針,是指向“垃圾”內存的指針。人們一般不會錯用NULL指針,因為用if語句很容易判斷。但是“野指針”是很危險的,if語句對它不起作用。
    “野指針”的成因主要有兩種:
    (1)指針變量沒有被初始化。任何指針變量剛被創(chuàng)建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。所以,指針變量在創(chuàng)建的同時應當被初始化,要么將指針設置為NULL,要么讓它指向合法的內存。例如
    char *p = NULL;
    char *str = (char *) malloc(100);
    (2)指針p被free或者delete之后,沒有置為NULL,讓人誤以為p是個合法的指針。參見7.5節(jié)。
    (3)指針操作超越了變量的作用范圍。這種情況讓人防不勝防,示例程序如下:
    class A
    {
    public:
    void Func(void){ cout << “Func of class A” << endl; }
    };
    void Test(void)
    {
    A *p;
    {
    A a;
    p = &a; // 注意 a 的生命期
    }
    p->Func(); // p是“野指針”
    }
    函數(shù)Test在執(zhí)行語句p->Func()時,對象a已經消失,而p是指向a的,所以p就成了“野指針”。但奇怪的是我運行這個程序時居然沒有出錯,這可能與編譯器有關。
    7.8 有了malloc/free為什么還要new/delete ?
    malloc與free是C++/C語言的標準庫函數(shù),new/delete是C++的運算符。它們都可用于申請動態(tài)內存和釋放內存。
    對于非內部數(shù)據類型的對象而言,光用maloc/free無法滿足動態(tài)對象的要求。對象在創(chuàng)建的同時要自動執(zhí)行構造函數(shù),對象在消亡之前要自動執(zhí)行析構函數(shù)。由于malloc/free是庫函數(shù)而不是運算符,不在編譯器控制權限之內,不能夠把執(zhí)行構造函數(shù)和析構函數(shù)的任務強加于malloc/free。
    因此C++語言需要一個能完成動態(tài)內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數(shù)。
    我們先看一看malloc/free和new/delete如何實現(xiàn)對象的動態(tài)內存管理,見示例7-8。
    class Obj
    {
    public :
    Obj(void){ cout << “Initialization” << endl; }
    ~Obj(void){ cout << “Destroy” << endl; }
    void Initialize(void){ cout << “Initialization” << endl; }
    void Destroy(void){ cout << “Destroy” << endl; }
    };
    void UseMallocFree(void)
    {
    Obj *a = (obj *)malloc(sizeof(obj)); // 申請動態(tài)內存
    a->Initialize(); // 初始化
    //…
    a->Destroy(); // 清除工作
    free(a); // 釋放內存
    }
    void UseNewDelete(void)
    {
    Obj *a = new Obj; // 申請動態(tài)內存并且初始化
    //…
    delete a; // 清除并且釋放內存
    }
    示例7-8 用malloc/free和new/delete如何實現(xiàn)對象的動態(tài)內存管理
    類Obj的函數(shù)Initialize模擬了構造函數(shù)的功能,函數(shù)Destroy模擬了析構函數(shù)的功能。函數(shù)UseMallocFree中,由于malloc/free不能執(zhí)行構造函數(shù)與析構函數(shù),必須調用成員函數(shù)Initialize和Destroy來完成初始化與清除工作。函數(shù)UseNewDelete則簡單得多。
    所以我們不要企圖用malloc/free來完成動態(tài)對象的內存管理,應該用new/delete。由于內部數(shù)據類型的“對象”沒有構造與析構的過程,對它們而言malloc/free和new/delete是等價的。
    既然new/delete的功能完全覆蓋了malloc/free,為什么C++不把malloc/free淘汰出局呢?這是因為C++程序經常要調用C函數(shù),而C程序只能用malloc/free管理動態(tài)內存。
    如果用free釋放“new創(chuàng)建的動態(tài)對象”,那么該對象因無法執(zhí)行析構函數(shù)而可能導致程序出錯。如果用delete釋放“malloc申請的動態(tài)內存”,理論上講程序不會出錯,但是該程序的可讀性很差。所以new/delete必須配對使用,malloc/free也一樣。
    7.9 內存耗盡怎么辦?
    如果在申請動態(tài)內存時找不到足夠大的內存塊,malloc和new將返回NULL指針,宣告內存申請失敗。通常有三種方式處理“內存耗盡”問題。
    (1)判斷指針是否為NULL,如果是則馬上用return語句終止本函數(shù)。例如:
    void Func(void)
    {
    A *a = new A;
    if(a == NULL)
    {
    return;
    }
    …
    }
    (2)判斷指針是否為NULL,如果是則馬上用exit(1)終止整個程序的運行。例如:
    void Func(void)
    {
    A *a = new A;
    if(a == NULL)
    {
    cout << “Memory Exhausted” << endl;
    exit(1);
    }
    …
    }
    (3)為new和malloc設置異常處理函數(shù)。例如Visual C++可以用_set_new_hander函數(shù)為new設置用戶自己定義的異常處理函數(shù),也可以讓malloc享用與new相同的異常處理函數(shù)。詳細內容請參考C++使用手冊。