天天看點

C++的記憶體管理

這篇文章是我在學習高品質C++/C程式設計指南中的第7章"記憶體管理"後的一篇筆記,之前我也寫過相關的文章指針以及記憶體配置設定,但我感覺那篇還不是很好,這篇我很把它更完善一些

  1. 從靜态區配置設定,一般是全局變量和static類型變量

  2.從棧區配置設定記憶體,一般是局部的變量,會随着所在函數的結束而自動釋放

  3.從堆中配置設定,一般是使用手動配置設定,使用malloc()函數和new來申請任意大小空間,不過要手動釋放空間,相應的使用free()函數和delete釋放,

    如果不釋放該空間,而且指向該空間的指針指向了别的空間.則該空間就無法釋放,造成記憶體洩露,造成了記憶體浪費

  1.在使用malloc()或new申請空間時,要檢查有沒有配置設定空間成功,判斷方法是判斷指針是否為NULL,如申請一塊很大的記憶體而沒有這麼大的記憶體則配置設定記憶體會失敗

  2.申請成功後最好是将該記憶體清空,使用memset()後ZeroMemory()清空,不然存在垃圾而造成有時候輸出很大亂碼

  3.不要忘記為數組和動态記憶體賦初值。防止将未被初始化的記憶體作為右值使用。(這句話不太了解)

  4.要防止數組或指針記憶體越界,

  5.申請記憶體成功後,使用結束後要釋放,系統不會自動釋放手動配置設定的記憶體

  6.記憶體釋放後,指針還是指向那塊位址,不過這指針已經是"野指針"了,是以釋放記憶體後指針要指向NULL,不然很危險,容易出錯,if()對野指針的判斷不起作用

  1. 數組裡的資料可以單個修改,但指針的不行,如我的例子,char str[] = "hello",數組的大小有6個字元(注意\0),可以通過str[0] = 'X'修改了的個字元,而指針

char *p = "Word",p是指向了一串常量的字元串,常量字元串是不可修改的,如 p[0] = 'X',編譯器編譯時不會儲存,但執行時會出錯

C++的記憶體管理

   2.内容的複制與比較

   内容的複制要使用strcpy()函數,不要使用指派符"=",内容的比較也是不要使用比較符号"<,>,==",使用strcmp()函數

// 數組…   

    char a[] = "hello";  

    char b[10];  

    strcpy(b, a);           // 不能用   b = a;   

    if(strcmp(b, a) == 0)   // 不能用  if (b == a)  

// 數組…

char a[] = "hello";

char b[10];

strcpy(b, a); // 不能用 b = a;

if(strcmp(b, a) == 0) // 不能用 if (b == a)

// 指針…   

   int len = strlen(a);  

   char *p = (char *)malloc(sizeof(char)*(len+1));  

   strcpy(p,a);            // 不要用 p = a;   

   if(strcmp(p, a) == 0)   // 不要用 if (p == a)  

// 指針…

int len = strlen(a);

char *p = (char *)malloc(sizeof(char)*(len+1));

strcpy(p,a); // 不要用 p = a;

if(strcmp(p, a) == 0) // 不要用 if (p == a)

    3,計算空間的大小

 對數組的計算是使用sizeof()函數,該函數會按照記憶體對齊的方式4的倍數計算,而指針的空間大小沒法計算,隻能記住在申請空間時的空間大小

注意當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指針,不論數組a的容量是多少,sizeof(a)始終等于sizeof(char *)

void Func(char a[100])  

    {  

        cout<< sizeof(a) << endl;   // 4位元組而不是100位元組   

}  

void Func(char a[100])

{

cout<< sizeof(a) << endl; // 4位元組而不是100位元組

}

如果函數的參數是指針,則不要使用該參數來申請記憶體空間,這樣沒有實際的用處,而且這樣當函數結束時還得不到釋放記憶體而造成記憶體洩露

C++的記憶體管理

     這個問題可以使用"指針的指針"的方法可以解決,不然使用傳回指針位址的辦法,先看一下使用 "指針的指針"方法,

還可以考慮一下引用

C++的記憶體管理

使用傳回記憶體位址的方法

C++的記憶體管理

 使用傳回的方式傳遞記憶體位址容易出錯的地方在于放回"棧記憶體"的指針,當GetMemory()函數結束時棧記憶體也被釋放,

C++的記憶體管理

 像這個代碼

char *GetString2(void)  

{  

    char *p = "hello world";  

    return p;  

void Test5(void)  

    char *str = NULL;  

    str = GetString2();  

    cout<< str << endl;  

char *GetString2(void)

char *p = "hello world";

return p;

void Test5(void)

char *str = NULL;

str = GetString2();

cout<< str << endl;

函數Test5運作雖然不會出錯,但是函數GetString2的設計概念卻是錯誤的。因為GetString2内的“hello world”是常量字元串,位于靜态存儲區,

它在程式生命期内恒定不變。無論什麼時候調用GetString2,它傳回的始終是同一個“隻讀”的記憶體塊。

   1. 當我們使用free()和delete釋放一塊記憶體時,指針還是指向原來的位址,不過這時候的指針時野指針,

可以驗證一下.這圖是我調試到if()語句時的情況,p還沒有指向NULL,隻是釋放了p指向的空間了

C++的記憶體管理

執行的結果可以看看...

C++的記憶體管理

是以有這樣的一些特征:

1.指針銷毀了,并不表示所指的空間也得到了釋放 :記憶體洩露

2.記憶體被釋放了,并不表示指針也被銷毀了或指向NULL :野指針

malloc與free是C++/C語言的标準庫函數,new/delete是C++的運算符。它們都可用于申請動态記憶體和釋放記憶體。對于非内部資料類型的對象而言,

光用maloc/free無法滿足動态對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由于malloc/free是庫函

數而不是運算符,不在編譯器控制權限之内,不能夠把執行構造函數和析構函數的任務強加于malloc/free。

是以C++語言需要一個能完成動态記憶體配置設定和初始化工作的運算符new,以及一個能完成清理與釋放記憶體工作的運算符delete。注意new/delete不是庫函數。

我們先看一看malloc/free和new/delete如何實作對象的動态記憶體管理,看代碼

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));   // 申請動态記憶體   

    a->Initialize();                        // 初始化   

    //…   

    a->Destroy();   // 清除工作   

    free(a);        // 釋放記憶體   

void UseNewDelete(void)  

    Obj  *a = new Obj;  // 申請動态記憶體并且初始化   

    delete a;           // 清除并且釋放記憶體   

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)); // 申請動态記憶體

a->Initialize(); // 初始化

//…

a->Destroy(); // 清除工作

free(a); // 釋放記憶體

void UseNewDelete(void)

Obj *a = new Obj; // 申請動态記憶體并且初始化

delete a; // 清除并且釋放記憶體

       類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也一樣。

   1.判斷指針是否為NULL,如果是則馬上用return語句終止本函數

   2.判斷指針是否為NULL,如果是則馬上用exit(1)終止整個程式的運作

   3.為new和malloc設定異常處理函數。例如Visual C++可以用_set_new_hander函數為new設定使用者自己定義的異常處理函數,

      也可以讓malloc享用與new相同的異常處理函數

malloc()/free()和new/delete的使用要點網上有更詳細的說明

繼續閱讀