1. 程式人生 > >C++之new、delete 與malloc、free

C++之new、delete 與malloc、free

在C/C++程式設計中經常會申請記憶體,而對記憶體的申請釋放操作有兩套方法: new、delete 與malloc、free。

1. 區別

(1). new、delete是c++中的操作符,malloc、free是C中的一個函式,它們都可用於申請動態記憶體和釋放記憶體。
(2). free對應的是malloc;delete對應的是new;free用來釋放malloc出來動態記憶體,delete用來釋放new出來的動態記憶體空間。他們的使用最好是成對使用,不要去混搭。
(3). new的效率malloc稍微低一些,可以認為new=(malloc+建構函式)。 new 不止是分配記憶體,而且會呼叫類的建構函式。new出來的指標是直接帶型別資訊的。相對應的delete會呼叫類的解構函式。
而malloc則只分配記憶體,malloc返回的都是void指標,不會進行初始化類成員的工作,相對應的free也不會呼叫解構函式。由於malloc/free是庫函式而不是運算子,不在編譯器控制權限之內,不能夠把執行建構函式和解構函式的任務強加於malloc/free。
(4). 記憶體洩漏對於malloc或者new都可以檢查出來的,區別在於new可以指明是哪個檔案的哪一行,而malloc沒有這些資訊。
(5). malloc不會拋異常,而new會;無法重定義malloc失敗時的預設返回NULL,但是new失敗時預設行為可以重定義,比如不讓其丟擲異常。
(6). 陣列的時候int p=(int

)malloc(10*sizeof(int)) 釋放的時候 free(p)即可;這是因為編譯器對malloc做了一些特殊的處理,以保證可以正確釋放記憶體。
而當int *p=new int[10]釋放的時候應為delete []p,注意[]的作用說明釋放的是一個數組的記憶體,如果delete p則只是釋放的p[0],其餘9個int的記憶體沒有釋放;這是因為當指明為[]的時候,編譯器實際上是做了一個迴圈來釋放這個陣列的所有記憶體。
(7). 在類和物件的時候會有很大區別。在使用malloc和free來處理動態記憶體的時候,僅僅是釋放了這個物件所佔的記憶體,而不會呼叫這個物件的解構函式;使用new和delete就可以既釋放物件的記憶體的同時,呼叫這個物件的解構函式。

2. free和delete共同之處

  • 它們都是隻把指標所指向的記憶體釋放掉了,並沒有把指標本身幹掉。在free和delete之後,都需要手動的把指向清理記憶體的指標置為空,即p=NULL,否則指標指向的記憶體空間雖然釋放了,但是指標p的值還是記錄的那塊地址,該地址對應的記憶體是垃圾,p就成了“野指標”。同樣會使人認為p是個合法的指標(p依舊指向一個地址),通常在程式中,使用一個指標前會檢查p!=NULL,指標未置為空就不作用。此時如果再釋放p指向的空間,編譯器就會報錯,因為釋放一個已經被釋放過的空間是不合法的。而將其置為NULL之後再重複釋放就不會產生問題,因為delete一個0(NULL)指標是安全的。
    即:
  • (1). 指標消亡了,並不表示它指示的動態記憶體會自動釋放;
  • (2). 動態記憶體釋放掉了,如果這個記憶體是一個動態物件,則並不表示一定會呼叫這個物件的解構函式;
  • (3). 動態記憶體釋放掉了,並且呼叫了解構函式,並不表示指標會消亡或者自動變成了NULL。

3 如果用new建立物件,再用free去釋放記憶體,結果會怎麼樣呢?

  • 根據定義:new()函式實際過程中做了兩步操作:第一步是分配記憶體空間,第二步是呼叫類的建構函式;delete()也同樣是兩步:第一步是呼叫類的解構函式,第二步才是釋放記憶體;而malloc()和free()僅僅是分配記憶體與釋放記憶體操作;
  • 那麼如果用free去釋放new分配的記憶體,就會缺少呼叫解構函式的步驟。因為,在建構函式裡面申請的記憶體因為沒有被解構函式釋放,所以該記憶體並沒有釋放,所以如果再輸出該記憶體的值,那麼應該還是原來設定的值。

4 free/delete 出錯可能

http://bbs.csdn.net/topics/390303069 有這樣一個例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
        char * s = (char *)malloc(100);
        s = "Hello world!";

        free(s);

        if(s == NULL)
                printf("Hello\n");
        else
                printf("no\n");

        return 0;
}

這中情況在執行時直接退出,在eclipse cdt中除錯時或出現這種情況:
指標申請後被修改再free會失敗
原因是因為:指標申請後被修改再free會失敗

char * s = (char *)malloc(100);

此時s指向堆中某區域;

s = "Hello world!";

s重新指向文字常量區某區域;此時,指標被修改!

free(s);

所以程式會崩潰。
同樣的問題會出現在

size_t iconv (iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)

函式中,outbuf在使用過程中不斷的被修改。
執行過iconv過程中,outbuf所指向的記憶體空間位置已經被改掉了, 所以你任何時候去讀**outbuf, 都是讀不到任何iconv後的資料的(都在*outbuf這個指標前面放著呢)。