九九热这里直有精品,1区二区三区在线播放,玖玖爱在线观看资源,国产aⅴ综合网,午夜福利男女,日本亚洲欧美三级,日韩无码黄色导航,内射少妇13区,中文字幕高清网

您身邊的軟件定制專家--9年開發(fā)經(jīng)驗為您護航

18678812288
0531-88887250

淺談 C++ 中的 new/delete 和 new[]/delete[]

文章作者:濟南軟件開發(fā) 時間:2016年11月08日

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

  new 和 delete 是函數(shù)嗎?

  new [] 和 delete [] 又是什么?什么時候用它們?

  你知道 operator new 和 operator delete 嗎?

  為什么 new [] 出來的數(shù)組有時可以用 delete 釋放有時又不行?

  …

  如果你對這些問題都有疑問的話,不妨看看我這篇文章。

  new 和 delete 到底是什么?

  如果找工作的同學看一些面試的書,我相信都會遇到這樣的題:sizeof 不是函數(shù),然后舉出一堆的理由來證明 sizeof 不是函數(shù)。在這里,和 sizeof 類似,new 和 delete 也不是函數(shù),它們都是 C++ 定義的關(guān)鍵字,通過特定的語法可以組成表達式。和 sizeof 不同的是,sizeof 在編譯時候就可以確定其返回值,new 和 delete 背后的機制則比較復雜。

  繼續(xù)往下之前,請你想想你認為 new 應該要做些什么?也許你第一反應是,new 不就和 C 語言中的 malloc 函數(shù)一樣嘛,就用來動態(tài)申請空間的。你答對了一半,看看下面語句:

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

  你就可以看出 new 和 malloc 還是有點不同的,malloc 申請完空間之后不會對內(nèi)存進行必要的初始化,而 new 可以。所以 new expression 背后要做的事情不是你想象的那么簡單。在我用實例來解釋 new 背后的機制之前,你需要知道 operator new 和 operator delete 是什么玩意。

  operator new 和 operator delete

  這兩個其實是 C++ 語言標準庫的庫函數(shù),原型分別如下:

  void *operator new(size_t);     //allocate an object

  void *operator delete(void *);    //free an object

  void *operator new[](size_t);     //allocate an array

  void *operator delete[](void *);    //free an array

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

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

  new 和 delete 背后機制

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

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

  class A

  {

  public:

  A(int v) : var(v)

  {

  fopen_s(&file, "test", "r");

  }

  ~A()

  {

  fclose(file);

  }

  private:

  int var;

  FILE *file;

  };

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

  我們使用

  class *pA = new A(10);

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

  簡單總結(jié)一下:

  首先需要調(diào)用上面提到的 operator new 標準庫函數(shù),傳入的參數(shù)為 class A 的大小,這里為 8 個字節(jié),至于為什么是 8 個字節(jié),你可以看看《深入 C++ 對象模型》一書,這里不做多解釋。這樣函數(shù)返回的是分配內(nèi)存的起始地址,這里假設是 0x007da290。

  上面分配的內(nèi)存是未初始化的,也是未類型化的,第二步就在這一塊原始的內(nèi)存上對類對象進行初始化,調(diào)用的是相應的構(gòu)造函數(shù),這里是調(diào)用 A:A(10); 這個函數(shù),從圖中也可以看到對這塊申請的內(nèi)存進行了初始化,var=10, file 指向打開的文件。

  最后一步就是返回新分配并構(gòu)造好的對象的指針,這里 pA 就指向 0x007da290 這塊內(nèi)存,pA 的類型為類 A 對象的指針。

  所有這三步,你都可以通過反匯編找到相應的匯編代碼,在這里我就不列出了。

  好了,那么 delete 都干了什么呢?還是接著上面的例子,如果這時想釋放掉申請的類的對象怎么辦?當然我們可以使用下面的語句來完成:

  delete pA;

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

  delete 就做了兩件事情:

  調(diào)用 pA 指向?qū)ο蟮奈鰳?gòu)函數(shù),對打開的文件進行關(guān)閉。

  通過上面提到的標準庫函數(shù) operator delete 來釋放該對象的內(nèi)存,傳入函數(shù)的參數(shù)為 pA 的值,也就是 0x007d290。

  好了,解釋完了 new 和 delete 背后所做的事情了,是不是覺得也很簡單?不就多了一個構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用嘛。

  如何申請和釋放一個數(shù)組?

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

  string *psa = new string[10];      //array of 10 empty strings

  int *pia = new int[10];           //array of 10 uninitialized ints

  上面在申請一個數(shù)組時都用到了 new [] 這個表達式來完成,按照我們上面講到的 new 和 delete 知識,第一個數(shù)組是 string 類型,分配了保存對象的內(nèi)存空間之后,將調(diào)用 string 類型的默認構(gòu)造函數(shù)依次初始化數(shù)組中每個元素;第二個是申請具有內(nèi)置類型的數(shù)組,分配了存儲 10 個 int 對象的內(nèi)存空間,但并沒有初始化。

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

  delete [] psa;

  delete [] pia;

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

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

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

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

  class A *pAa = new A[3];

  時需要做的事情如下:

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

  這樣的話,釋放就很簡單了:

  delete pAa;

  這里要注意的兩點是:

  調(diào)用析構(gòu)函數(shù)的次數(shù)是從數(shù)組對象指針前面的 4 個字節(jié)中取出;

  傳入 operator delete[] 函數(shù)的參數(shù)不是數(shù)組對象的指針 pAa,而是 pAa 的值減 4。

  為什么 new/delete 、new []/delete[] 要配對使用?

  其實說了這么多,還沒到我寫這篇文章的最原始意圖。從上面解釋的你應該懂了 new/delete、new[]/delete[] 的工作原理了,因為它們之間有差別,所以需要配對使用。但偏偏問題不是這么簡單,這也是我遇到的問題,如下這段代碼:

  int *pia = new int[10];

  delete []pia;

  這肯定是沒問題的,但如果把 delete []pia; 換成 delete pia; 的話,會出問題嗎?

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

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

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

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

  delete pAa;

  那么 delete pAa; 做了兩件事:

  調(diào)用一次 pAa 指向的對象的析構(gòu)函數(shù);

  調(diào)用 operator delete(pAa); 釋放內(nèi)存。

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

  上面的問題你如果說沒關(guān)系的話,那么第二點就是致命的了!直接釋放 pAa 指向的內(nèi)存空間,這個總是會造成嚴重的段錯誤,程序必然會奔潰!因為分配的空間的起始地址是 pAa 指向的地方減去 4 個字節(jié)的地方。你應該傳入?yún)?shù)設為那個地址!


想要了解更多詳情歡迎來電咨詢18678812288
登陸網(wǎng)址:m.h6244.cn。
聯(lián)系人:王經(jīng)理。

噶尔县| 莱阳市| 特克斯县| 广水市| 嘉义县| 汝阳县| 迁西县| 榆社县| 德州市| 昌图县| 临泽县| 玉田县| 子洲县| 江陵县| 淮阳县| 丰都县| 威信县| 东乌| 同江市| 砚山县| 阳高县| 安泽县| 临猗县| 新蔡县| 漳浦县| 清远市| 株洲市| 花垣县| 林州市| 无棣县| 辽源市| 南川市| 塔河县| 收藏| 仙桃市| 开江县| 荔波县| 株洲县| 西乌珠穆沁旗| 固镇县| 金川县|