日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

浅谈 C++ 中的 new/delete 和 new[]/delete[]

發(fā)布時(shí)間:2023/12/20 c/c++ 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈 C++ 中的 new/delete 和 new[]/delete[] 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

來(lái)自:http://www.cnblogs.com/hazir/p/new_and_delete.html

在 C++ 中,你也許經(jīng)常使用 new 和 delete 來(lái)動(dòng)態(tài)申請(qǐng)和釋放內(nèi)存,但你可曾想過(guò)以下問(wèn)題呢?

  • new 和 delete 是函數(shù)嗎?
  • new [] 和 delete [] 又是什么?什么時(shí)候用它們?
  • 你知道 operator new 和 operator delete 嗎?
  • 為什么 new [] 出來(lái)的數(shù)組有時(shí)可以用 delete 釋放有時(shí)又不行?

如果你對(duì)這些問(wèn)題都有疑問(wèn)的話,不妨看看我這篇文章。

new 和 delete 到底是什么?

如果找工作的同學(xué)看一些面試的書(shū),我相信都會(huì)遇到這樣的題:sizeof 不是函數(shù),然后舉出一堆的理由來(lái)證明 sizeof 不是函數(shù)。在這里,和 sizeof 類似,new 和 delete 也不是函數(shù),它們都是 C++ 定義的關(guān)鍵字,通過(guò)特定的語(yǔ)法可以組成表達(dá)式。

和 sizeof 不同的是,sizeof 在編譯時(shí)候就可以確定其返回值,new 和 delete 背后的機(jī)制則比較復(fù)雜。
繼續(xù)往下之前,請(qǐng)你想想你認(rèn)為 new 應(yīng)該要做些什么?也許你第一反應(yīng)是,new 不就和 C 語(yǔ)言中的 malloc 函數(shù)一樣嘛,就用來(lái)動(dòng)態(tài)申請(qǐng)空間的。你答對(duì)了一半,看看下面語(yǔ)句:

string *ps = new string("hello world");

你就可以看出 new 和 malloc 還是有點(diǎn)不同的,malloc 申請(qǐng)完空間之后不會(huì)對(duì)內(nèi)存進(jìn)行必要的初始化,而 new 可以。

所以?new?expression?背后要做的事情不是你想象的那么簡(jiǎn)單。在我用實(shí)例來(lái)解釋 new 背后的機(jī)制之前,你需要知道?operator new?和?operator delete?是什么玩意。

operator new 和 operator delete

這兩個(gè)其實(shí)是 C++ 語(yǔ)言標(biāo)準(zhǔn)庫(kù)的庫(kù)函數(shù),原型分別如下:

void *operator new(size_t); //allocate an object void *operator delete(void *); //free an objectvoid *operator new[](size_t); //allocate an array void *operator delete[](void *); //free an array

后面兩個(gè)你可以先不看,后面再介紹。前面兩個(gè)均是 C++ 標(biāo)準(zhǔn)庫(kù)函數(shù),你可能會(huì)覺(jué)得這是函數(shù)嗎?請(qǐng)不要懷疑,這就是函數(shù)!C++ Primer?一書(shū)上說(shuō)這不是重載 new 和 delete 表達(dá)式(如?operator=?就是重載?=?操作符),因?yàn)?new 和 delete 是不允許重載的。但我還沒(méi)搞清楚為什么要用 operator new 和 operator delete 來(lái)命名,比較費(fèi)解。我們只要知道它們的意思就可以了,這兩個(gè)函數(shù)和 C 語(yǔ)言中的 malloc 和 free 函數(shù)有點(diǎn)像了,都是用來(lái)申請(qǐng)和釋放內(nèi)存的,并且 operator new 申請(qǐng)內(nèi)存之后不對(duì)內(nèi)存進(jìn)行初始化,直接返回申請(qǐng)內(nèi)存的指針。

我們可以直接在我們的程序中使用這幾個(gè)函數(shù)。

new 和 delete 背后機(jī)制

知道上面兩個(gè)函數(shù)之后,我們用一個(gè)實(shí)例來(lái)解釋 new 和 delete 背后的機(jī)制:

我們不用簡(jiǎn)單的 C++ 內(nèi)置類型來(lái)舉例,使用復(fù)雜一點(diǎn)的類類型,定義一個(gè)類 A:

class A { public:A(int v) : var(v){fopen_s(&file, "test", "r");}~A(){fclose(file);}private:int var;FILE *file; };

很簡(jiǎn)單,類 A 中有兩個(gè)私有成員,有一個(gè)構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù),構(gòu)造函數(shù)中初始化私有變量 var 以及打開(kāi)一個(gè)文件,析構(gòu)函數(shù)關(guān)閉打開(kāi)的文件。

我們使用

class A *pA = new A(10);

來(lái)創(chuàng)建一個(gè)類的對(duì)象,返回其指針 pA。如下圖所示 new 背后完成的工作:

簡(jiǎn)單總結(jié)一下:

  • 首先需要調(diào)用上面提到的 operator new 標(biāo)準(zhǔn)庫(kù)函數(shù),傳入的參數(shù)為 class A 的大小,這里為 8 個(gè)字節(jié),至于為什么是 8 個(gè)字節(jié),你可以看看《深入 C++ 對(duì)象模型》一書(shū),這里不做多解釋。這樣函數(shù)返回的是分配內(nèi)存的起始地址,這里假設(shè)是 0x007da290。
  • 上面分配的內(nèi)存是未初始化的,也是未類型化的,第二步就在這一塊原始的內(nèi)存上對(duì)類對(duì)象進(jìn)行初始化,調(diào)用的是相應(yīng)的構(gòu)造函數(shù),這里是調(diào)用?A:A(10);?這個(gè)函數(shù),從圖中也可以看到對(duì)這塊申請(qǐng)的內(nèi)存進(jìn)行了初始化,var=10, file 指向打開(kāi)的文件
  • 最后一步就是返回新分配并構(gòu)造好的對(duì)象的指針,這里 pA 就指向 0x007da290 這塊內(nèi)存,pA 的類型為類 A 對(duì)象的指針。
  • 所有這三步,你都可以通過(guò)反匯編找到相應(yīng)的匯編代碼,在這里我就不列出了。

    好了,那么 delete 都干了什么呢?還是接著上面的例子,如果這時(shí)想釋放掉申請(qǐng)的類的對(duì)象怎么辦?當(dāng)然我們可以使用下面的語(yǔ)句來(lái)完成:

    delete pA;

    delete 所做的事情如下圖所示:

    delete 就做了兩件事情:

  • 調(diào)用 pA 指向?qū)ο蟮奈鰳?gòu)函數(shù),對(duì)打開(kāi)的文件進(jìn)行關(guān)閉。
  • 通過(guò)上面提到的標(biāo)準(zhǔn)庫(kù)函數(shù) operator delete 來(lái)釋放該對(duì)象的內(nèi)存,傳入函數(shù)的參數(shù)為 pA 的值,也就是 0x007d290。
  • 好了,解釋完了 new 和 delete 背后所做的事情了,是不是覺(jué)得也很簡(jiǎn)單?不就多了一個(gè)構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用嘛。

    如何申請(qǐng)和釋放一個(gè)數(shù)組?

    我們經(jīng)常要用到動(dòng)態(tài)分配一個(gè)數(shù)組,也許是這樣的:

    string *psa = new string[10]; //array of 10 empty strings int *pia = new int[10]; //array of 10 uninitialized ints

    上面在申請(qǐng)一個(gè)數(shù)組時(shí)都用到了?new []?這個(gè)表達(dá)式來(lái)完成,按照我們上面講到的 new 和 delete 知識(shí),第一個(gè)數(shù)組是 string 類型,分配了保存對(duì)象的內(nèi)存空間之后,將調(diào)用 string 類型的默認(rèn)構(gòu)造函數(shù)依次初始化數(shù)組中每個(gè)元素;

    第二個(gè)是申請(qǐng)具有內(nèi)置類型的數(shù)組,分配了存儲(chǔ) 10 個(gè) int 對(duì)象的內(nèi)存空間,但并沒(méi)有初始化。

    如果我們想釋放空間了,可以用下面兩條語(yǔ)句:

    delete [] psa; delete [] pia;

    都用到?delete []?表達(dá)式,注意這地方的 [] 一般情況下不能漏掉!我們也可以想象這兩個(gè)語(yǔ)句分別干了什么:第一個(gè)對(duì) 10 個(gè) string 對(duì)象分別調(diào)用析構(gòu)函數(shù),然后再釋放掉為對(duì)象分配的所有內(nèi)存空間;第二個(gè)因?yàn)槭莾?nèi)置類型不存在析構(gòu)函數(shù),直接釋放為 10 個(gè) int 型分配的所有內(nèi)存空間。

    這里對(duì)于第一種情況就有一個(gè)問(wèn)題了:我們?nèi)绾沃?psa 指向?qū)ο蟮臄?shù)組的大小?怎么知道調(diào)用幾次析構(gòu)函數(shù)?

    這個(gè)問(wèn)題直接導(dǎo)致我們需要在 new [] 一個(gè)對(duì)象數(shù)組時(shí),需要保存數(shù)組的維度,C++ 的做法是在分配數(shù)組空間時(shí)多分配了 4 個(gè)字節(jié)的大小,專門(mén)保存數(shù)組的大小,在 delete [] 時(shí)就可以取出這個(gè)保存的數(shù),就知道了需要調(diào)用析構(gòu)函數(shù)多少次了。

    還是用圖來(lái)說(shuō)明比較清楚,我們定義了一個(gè)類 A,但不具體描述類的內(nèi)容,這個(gè)類中有顯示的構(gòu)造函數(shù)、析構(gòu)函數(shù)等。那么 當(dāng)我們調(diào)用

    class A *pAa = new A[3];

    時(shí)需要做的事情如下:

    從這個(gè)圖中我們可以看到申請(qǐng)時(shí)在數(shù)組對(duì)象的上面還多分配了 4 個(gè)字節(jié)用來(lái)保存數(shù)組的大小,但是最終返回的是對(duì)象數(shù)組的指針,而不是所有分配空間的起始地址。

    這樣的話,釋放就很簡(jiǎn)單了:

    delete []pAa;

    這里要注意的兩點(diǎn)是:

    • 調(diào)用析構(gòu)函數(shù)的次數(shù)是從數(shù)組對(duì)象指針前面的 4 個(gè)字節(jié)中取出;
    • 傳入?operator delete[]?函數(shù)的參數(shù)不是數(shù)組對(duì)象的指針 pAa,而是 pAa 的值減 4。

    為什么 new/delete 、new []/delete[] 要配對(duì)使用?

    其實(shí)說(shuō)了這么多,還沒(méi)到我寫(xiě)這篇文章的最原始意圖。從上面解釋的你應(yīng)該懂了 new/delete、new[]/delete[] 的工作原理了,因?yàn)樗鼈冎g有差別,所以需要配對(duì)使用。但偏偏問(wèn)題不是這么簡(jiǎn)單,這也是我遇到的問(wèn)題,如下這段代碼:

    int *pia = new int[10]; delete []pia;

    這肯定是沒(méi)問(wèn)題的,但如果把?delete []pia;?換成?delete pia;?的話,會(huì)出問(wèn)題嗎?

    這就涉及到上面一節(jié)沒(méi)提到的問(wèn)題了。上面我提到了在?new []?時(shí)多分配 4 個(gè)字節(jié)的緣由,因?yàn)槲鰳?gòu)時(shí)需要知道數(shù)組的大小,但如果不調(diào)用析構(gòu)函數(shù)呢(如內(nèi)置類型,這里的 int 數(shù)組)?我們?cè)?new []?時(shí)就沒(méi)必要多分配那 4 個(gè)字節(jié), delete [] 時(shí)直接到第二步釋放為 int 數(shù)組分配的空間。如果這里使用?delete pia;那么將會(huì)調(diào)用?operator delete?函數(shù),傳入的參數(shù)是分配給數(shù)組的起始地址,所做的事情就是釋放掉這塊內(nèi)存空間。不存在問(wèn)題的。

    這里說(shuō)的使用?new []?用 delete 來(lái)釋放對(duì)象的提前是:對(duì)象的類型是內(nèi)置類型或者是無(wú)自定義的析構(gòu)函數(shù)的類類型!

    我們看看如果是帶有自定義析構(gòu)函數(shù)的類類型,用?new []?來(lái)創(chuàng)建類對(duì)象數(shù)組,而用 delete 來(lái)釋放會(huì)發(fā)生什么?用上面的例子來(lái)說(shuō)明:

    class A *pAa = new class A[3]; delete pAa;

    那么?delete pAa;?做了兩件事:

    • 調(diào)用一次 pAa 指向的對(duì)象的析構(gòu)函數(shù);
    • 調(diào)用?operator delete(pAa);?釋放內(nèi)存。

    顯然,這里只對(duì)數(shù)組的第一個(gè)類對(duì)象調(diào)用了析構(gòu)函數(shù),后面的兩個(gè)對(duì)象均沒(méi)調(diào)用析構(gòu)函數(shù),如果類對(duì)象中申請(qǐng)了大量的內(nèi)存需要在析構(gòu)函數(shù)中釋放,而你卻在銷毀數(shù)組對(duì)象時(shí)少調(diào)用了析構(gòu)函數(shù),這會(huì)造成內(nèi)存泄漏。

    上面的問(wèn)題你如果說(shuō)沒(méi)關(guān)系的話,那么第二點(diǎn)就是致命的了!直接釋放 pAa 指向的內(nèi)存空間,這個(gè)總是會(huì)造成嚴(yán)重的段錯(cuò)誤,程序必然會(huì)奔潰!因?yàn)榉峙涞目臻g的起始地址是 pAa 指向的地方減去 4 個(gè)字節(jié)的地方。你應(yīng)該傳入?yún)?shù)設(shè)為那個(gè)地址!

    同理,你可以分析如果使用 new 來(lái)分配,用?delete []?來(lái)釋放會(huì)出現(xiàn)什么問(wèn)題?是不是總會(huì)導(dǎo)致程序錯(cuò)誤?

    總的來(lái)說(shuō),記住一點(diǎn)即可:new/delete、new[]/delete[] 要配套使用總是沒(méi)錯(cuò)的!

    總結(jié)

    以上是生活随笔為你收集整理的浅谈 C++ 中的 new/delete 和 new[]/delete[]的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。