new和(he)malloc的用法和(he)區別
時(shi)間:2025-01-22 來源:華清遠見(jian)
一.c++中new的三種使用方法包(bao)括:plain new, nothrow new 和 placement new。
1. plain new 就是我(wo)們(men)最常(chang)使用(yong)的new的方式,在C++中的定義如下(xia):
1.1 void* operator new(std::size_t) throw(std::bad_alloc);
1.2 void operator delete( void *) throw();
plain new在(zai)分配失敗(bai)的情況下,拋出(chu)異常std::bad_alloc而不(bu)是返回(hui)NULL。
2. nothrow new 是不拋出異常(chang)的運算(suan)符new的形式(shi)。nothrow new在失敗時,返回NULL。定義如(ru)下:
2.1 void * operator new(std::size_t, const std::nothrow_t&) throw();
2.2 void operator delete(void*) throw();
3. placement new 意即(ji)“放(fang)置”,這種new允許在一(yi)塊已經分配(pei)成功的(de)內(nei)存上重新構造對象或對象數組。placement new不(bu)用擔心內(nei)存分配(pei)失敗(bai),因為它根(gen)本不(bu)分配(pei)內(nei)存,它做的(de)唯一(yi)一(yi)件事情就是調用對象的(de)構造函數。定義(yi)如下:
3.1 void* operator new(size_t, void*);
3.2 void operator delete(void*, void*);
palcement new 的(de)主要用(yong)途就是反復使用(yong)已分配(pei)的(de)內存,避免多次(ci)分配(pei)的(de)開銷和風險。
二.malloc的用法
1.概念
malloc函(han)數在C語(yu)言中用(yong)于動態分配內(nei)存(cun)。其全稱是memory allocation,用(yong)于申請(qing)一(yi)塊連續(xu)的(de)指定大小的(de)內(nei)存(cun)塊區域,并以void*類型(xing)返回(hui)分配的(de)內(nei)存(cun)區域地址。malloc函(han)數的(de)原型(xing)為:
void *malloc(unsigned int size);
該函數(shu)在內存的動態存儲(chu)區中分(fen)(fen)配(pei)一個長(chang)度為size的連續空間(jian)。如果(guo)分(fen)(fen)配(pei)成功(gong),則(ze)返回(hui)指向被分(fen)(fen)配(pei)內存的指針(zhen);如果(guo)分(fen)(fen)配(pei)失(shi)敗,則(ze)返回(hui)空指針(zhen)NULL。malloc函數(shu)返回(hui)的指針(zhen)需要通過(guo)類(lei)型轉(zhuan)換(huan)才能用于具體的數(shu)據對(dui)象。
1. 分配(pei)內存:使(shi)用(yong)malloc函數(shu)分配(pei)指定大小的內存塊。例如(ru),分配(pei)100個字節的內存:
int *ptr = (int*)malloc(100 * sizeof(int));
三. new和(he)malloc的區別
1. 申請的(de)內存所在位置
new操作符從自(zi)由存(cun)儲區(qu)(free store)上(shang)為對象動態(tai)分配(pei)內存(cun)空(kong)間,而malloc函數從堆上(shang)動態(tai)分配(pei)內存(cun)。
自由(you)存(cun)(cun)(cun)儲(chu)區是(shi)C++基于new操(cao)作符的(de)一個抽象概念,凡是(shi)通過new操(cao)作符進行內(nei)(nei)存(cun)(cun)(cun)申請,該內(nei)(nei)存(cun)(cun)(cun)即為自由(you)存(cun)(cun)(cun)儲(chu)區。而堆是(shi)操(cao)作系(xi)統中的(de)術語(yu),是(shi)操(cao)作系(xi)統所維護的(de)一塊特殊內(nei)(nei)存(cun)(cun)(cun),用(yong)于程序(xu)的(de)內(nei)(nei)存(cun)(cun)(cun)動態分(fen)配(pei),C語(yu)言(yan)使用(yong)malloc從堆上分(fen)配(pei)內(nei)(nei)存(cun)(cun)(cun),使用(yong)free釋放已分(fen)配(pei)的(de)對(dui)應(ying)內(nei)(nei)存(cun)(cun)(cun)。
那么自由存(cun)儲(chu)區(qu)是(shi)否能夠是(shi)堆(問題(ti)等價(jia)于(yu)(yu)new是(shi)否能在堆上動(dong)態分配內存(cun)),這取決于(yu)(yu)operator new 的實現細(xi)節。自由存(cun)儲(chu)區(qu)不(bu)僅可(ke)以是(shi)堆,還可(ke)以是(shi)靜(jing)態存(cun)儲(chu)區(qu),這都(dou)看operator new在哪里為對象分配內存(cun)。
特(te)別的,new甚至可以不為(wei)對象分配內(nei)存!定位(wei)new的功能可以辦到這一點(dian):
new (place_address) type
place_address為一(yi)個指針,代(dai)表一(yi)塊(kuai)內存的地(di)址。當使用(yong)(yong)上面這種僅以一(yi)個地(di)址調(diao)用(yong)(yong)new操作(zuo)符時(shi),new操作(zuo)符調(diao)用(yong)(yong)特殊的operator new,也就是下面這個版本:
void * operator new (size_t,void *) //不(bu)允許重(zhong)定(ding)義這個(ge)版(ban)本的operator new
這個operator new不分配(pei)任(ren)何的(de)內存(cun),它只是簡單地(di)返(fan)回指針實參,然后右new表(biao)達式負責在place_address指定的(de)地(di)址進(jin)行對象的(de)初(chu)始化(hua)工作。
2.返回類型安全(quan)性
new操作(zuo)(zuo)符內(nei)存(cun)(cun)分配(pei)成功(gong)時,返回的(de)是(shi)對象類型(xing)的(de)指針,類型(xing)嚴格(ge)與對象匹配(pei),無須進行類型(xing)轉(zhuan)(zhuan)換,故new是(shi)符合類型(xing)安全性的(de)操作(zuo)(zuo)符。而malloc內(nei)存(cun)(cun)分配(pei)成功(gong)則是(shi)返回void * ,需(xu)要通過強(qiang)制類型(xing)轉(zhuan)(zhuan)換將void*指針轉(zhuan)(zhuan)換成我們需(xu)要的(de)類型(xing)。
3.內存分配(pei)失敗時(shi)的返回值
new內存分配(pei)失敗時,會拋出bad_alloc異常,它不會返(fan)(fan)回(hui)NULL;malloc分配(pei)內存失敗時返(fan)(fan)回(hui)NULL。
在使用C語言(yan)時,我(wo)們習慣在malloc分配(pei)內存后(hou)判斷(duan)分配(pei)是(shi)否成功:示例如下:
int *a = (int *)malloc ( sizeof (int ));
if(NULL == a){
…
}
else
{
...
}
c++中(zhong)正確(que)的做法應該是使(shi)用異(yi)常機制:
try
{
int *a = new int();
}catch (bad_alloc &obj)
{
...
}
4.是否需(xu)要指(zhi)定內存大(da)小
使用new操作符申(shen)請內存分配時無須指(zhi)定內存塊的大(da)小,編(bian)譯器會(hui)根據類(lei)型信息(xi)自行計算,而malloc則需(xu)(xu)要(yao)顯式地指(zhi)出所需(xu)(xu)內存的尺(chi)寸。
class Demo{...
};
int main()
{
Demo * ptr = new Demo ;
Demo * ptr = (Demo *)malloc(sizeof(Demo )); //需要顯式指定所需內(nei)存大小sizeof(Demo );
}
當(dang)然了,我這里使用malloc來為(wei)我們(men)自定義類型分配內存是不(bu)怎(zen)么合適的,請看下(xia)一條。
5.是(shi)否調用構造函數/析構函數
使用new操作符來分配對象(xiang)內存時會經歷三個步驟:
第一(yi)步(bu):調用operator new 函數(對(dui)于數組是operator new[])分配一(yi)塊足(zu)夠大的(de),原始(shi)的(de),未命名的(de)內存空(kong)間以便存儲特定類型的(de)對(dui)象。
第二步:編(bian)譯器運(yun)行相(xiang)應的構造(zao)函數(shu)以構造(zao)對(dui)象,并為其傳入初值。
第三部(bu):對(dui)象構造完成后(hou),返回一個指向該對(dui)象的(de)指針。
使(shi)用delete操作(zuo)符來釋放對(dui)象(xiang)內存(cun)時會經歷兩個步(bu)驟(zou):
第(di)一步:調用對(dui)象的析構函數。
第二步(bu):編(bian)譯器調用(yong)operator delete(或operator delete[])函數(shu)釋放內存空間。
總之來說(shuo),new/delete會(hui)調用對象(xiang)的(de)構(gou)造函數/析構(gou)函數以完(wan)成對象(xiang)的(de)構(gou)造/析構(gou)。而malloc則不會(hui)。示(shi)例如(ru)下:
class Demo
{
public:
Demo() :a(1), b(1.2)
{
cout << __LINE__ << ":" << __func__ << endl;
}
private:
int a;
double b;
};
int main()
{
Demo * ptr = (Demo*)malloc(sizeof(Demo)); //構造函數(shu)沒有調用
return 0;
}
而使(shi)用new來(lai)分配(pei)對象時,示例如下:
class Demo
{
public:
Demo() :a(1), b(1.2)
{
cout << a << ":" << b << endl;
}
private:
int a;
double b;
};
int main()
{
Demo * ptr = new Demo;
return 0;
}

6.對數組的(de)處(chu)理
C++提供(gong)了new[]與delete[]來專門(men)處理數組類型:
A * ptr = new A[10];//分配10個A對象
使用new[]分配的內存必須使用delete[]進行(xing)釋放(fang):
delete [] ptr;
new對(dui)數(shu)(shu)(shu)組(zu)的支持體現在它會分別調用構造函數(shu)(shu)(shu)函數(shu)(shu)(shu)初始化每一(yi)個數(shu)(shu)(shu)組(zu)元素(su),釋放對(dui)象(xiang)(xiang)時為每個對(dui)象(xiang)(xiang)調用析構函數(shu)(shu)(shu)。注(zhu)意(yi)delete[]要(yao)與new[]配套(tao)使用,不然會找出(chu)數(shu)(shu)(shu)組(zu)對(dui)象(xiang)(xiang)部(bu)分釋放的現象(xiang)(xiang),造成(cheng)內存泄漏。
至于(yu)malloc,它并知道你在這塊(kuai)內(nei)存(cun)上要放(fang)的數組(zu)還是(shi)啥別的東西,反正(zheng)它就(jiu)給你一塊(kuai)原始的內(nei)存(cun),在給你個內(nei)存(cun)的地址就(jiu)完事。所以(yi)如(ru)果要動態分配一個數組(zu)的內(nei)存(cun),還需要我(wo)們手動指定數組(zu)的大小:
int * ptr = (int *) malloc( sizeof(int)* 10 );//分(fen)配一個(ge)10個(ge)int元素的(de)數組(zu)
7.new與malloc是否可以(yi)相互調用
operator new /operator delete的(de)實(shi)現可以(yi)基于malloc,而malloc的(de)實(shi)現不可以(yi)去調用new。下面是編(bian)寫operator new /operator delete 的(de)一種簡單方式,其(qi)他版本也與(yu)之類似:
1. void * operator new (sieze_t size)
2. {
3. if(void * mem = malloc(size)
4. return mem;
5. else
6. throw bad_alloc();
7. }
8. void operator delete(void *mem) noexcept
9. {
10. free(mem);
11. }
8.是否(fou)可以被重載
opeartor new /operator delete可以被重(zhong)載。標準庫(ku)是(shi)定義(yi)了operator new函數(shu)和operator delete函數(shu)的8個重(zhong)載版本(ben):
//這些(xie)版本可能拋出(chu)異常
1. void * operator new(size_t);
2. void * operator new[](size_t);
3. void * operator delete (void * )noexcept;
4. void * operator delete[](void *0)noexcept;
//這些(xie)版本承諾不拋出異常
5. void * operator new(size_t ,nothrow_t&) noexcept;
6. void * operator new[](size_t, nothrow_t& );
7. void * operator delete (void *,nothrow_t& )noexcept;
8. void * operator delete[](void *0,nothrow_t& )noexcept;
我們(men)可(ke)以自(zi)定義上面函數版本(ben)中的任意一個,前提是自(zi)定義版本(ben)必須位于全局(ju)作(zuo)用域或者類(lei)作(zuo)用域中。太細節(jie)的東西不在這里(li)講述,總之,我們(men)知(zhi)道我們(men)有(you)足(zu)夠的自(zi)由去重載operator new /operator delete ,以決定我們(men)的new與delete如(ru)何為對象分(fen)配內存,如(ru)何回收(shou)對象。
而malloc/free并不允(yun)許重載。
9. 能(neng)夠直觀(guan)地重(zhong)新分配(pei)內存
使(shi)(shi)用malloc分配(pei)的內(nei)存(cun)(cun)(cun)后,如(ru)果在使(shi)(shi)用過程中(zhong)發現(xian)內(nei)存(cun)(cun)(cun)不足,可以使(shi)(shi)用realloc函數進(jin)行內(nei)存(cun)(cun)(cun)重新(xin)分配(pei)實現(xian)內(nei)存(cun)(cun)(cun)的擴(kuo)充。realloc先判斷當前的指針所指內(nei)存(cun)(cun)(cun)是(shi)否有(you)(you)足夠的連續空(kong)間(jian),如(ru)果有(you)(you),原(yuan)地(di)擴(kuo)大可分配(pei)的內(nei)存(cun)(cun)(cun)地(di)址,并且返回原(yuan)來的地(di)址指針;如(ru)果空(kong)間(jian)不夠,先按照新(xin)指定的大小分配(pei)空(kong)間(jian),將(jiang)原(yuan)有(you)(you)數據從頭到(dao)尾(wei)拷貝到(dao)新(xin)分配(pei)的內(nei)存(cun)(cun)(cun)區域,而(er)后釋放原(yuan)來的內(nei)存(cun)(cun)(cun)區域。
new沒有這樣直(zhi)觀的配套設施來(lai)擴充內存。
10. 客戶(hu)處理(li)內(nei)存分配不足(zu)
在operator new拋出異常以反映一個(ge)未獲得(de)滿足的需求之前(qian),它會先(xian)調用一個(ge)用戶(hu)指定(ding)的錯誤處理(li)函數,這就是new-handler。new_handler是一個(ge)指針類型:
1. namespace std
2. {
3. typedef void (*new_handler)();
4. }
指向了一(yi)個(ge)沒有參(can)數(shu)沒有返回值(zhi)的函數(shu),即(ji)為錯誤處理函數(shu)。為了指定錯誤處理函數(shu),客戶需要調(diao)用(yong)set_new_handler,這(zhe)是一(yi)個(ge)聲明的一(yi)個(ge)標準庫函數(shu):
1. namespace std
2. {
3. new_handler set_new_handler(new_handler p ) throw();
4. }
set_new_handler的(de)參數(shu)為new_handler指針,指向了(le)operator new 無法分配(pei)足夠內存時該(gai)調用(yong)的(de)函數(shu)。其(qi)返回值也(ye)是個指針,指向set_new_handler被調用(yong)前正在執(zhi)行(xing)(但馬上就要發(fa)生替換)的(de)那個new_handler函數(shu)。
對(dui)于malloc,客戶并不(bu)(bu)能夠去編程決定內(nei)存不(bu)(bu)足(zu)以分配時(shi)要干(gan)什么事,只能看著malloc返回NULL。
總結
將上面所(suo)述(shu)的10點差別整(zheng)理成表格:


