天天看點

讀書筆記 effective c++ Item 16 成對使用new和delete時要用相同的形式

1. 一個錯誤釋放記憶體的例子

下面的場景會有什麼錯?

1 std::string *stringArray = new std::string[100];
2 
3 ...
4 
5 delete stringArray      

一切看上去都是有序的。new比對了一個delete。但有一些地方确實是錯了。程式的行為是未定義的。至少來說,stringArray指向的100個string對象中的99個看上去都不能被正确釋放,因為他們的析構函數可能永遠不會被調用。

2. 使用new 和delete時究竟做了啥?

當你使用一個new表達式(通過使用new動态的建立一個對象)時,會發生兩件事情。第一,記憶體被配置設定(通過一個叫做operator new的函數,看Item 49和Item 51)。第二,在配置設定的記憶體上調用了一個或多個構造函數。當你使用一個delete表達式時,另外兩件事情會發生:在記憶體上調用了一個或者多個析構函數,然後記憶體被解除配置設定(通過調用叫做operator delete的函數,見 Item 51)。關于delete的一個重要的問題是:在即将被删除的記憶體中究竟有多少對象?這個問題的答案決定了有多少個析構函數必須被調用。

3. new和delete不配對使用為啥會出錯?

實際上,下面這個問題更加簡單:被删除的指針是指向一個單獨的對象還是指向數組的所有對象?這是個關鍵的問題,因為單個對象的記憶體配置設定通常情況下同數組的記憶體配置設定是不一樣的。特别的,一個數組的記憶體通常包含了數組的大小,是以delete很容易就會知道需要調用多少個析構函數。單個對象的記憶體卻沒有這樣的資訊。你可以将記憶體不同配置設定想象成下面這個樣子,n是數組的大小:

讀書筆記 effective c++ Item 16 成對使用new和delete時要用相同的形式

當然這隻是一個例子。編譯器不需要這麼實作,雖然很多編譯器确實是這麼實作的。

當你在一個指針上使用delete時,delete能夠知道數組容量資訊是否存在的唯一方法就是通過你來告訴它。如果當你使用delete時用了“[]”,delete認為指針指向一個數組。否則,它會認為它在指向一個單一的對象:

1 std::string *stringPtr1 = new std::string;
2 
3 std::string *stringPtr2 = new std::string[100];
4 
5 ...
6 
7 delete stringPtr1; // delete an object
8 
9 delete [] stringPtr2; // delete an array of objects      

4. new和delete不配對使用會有什麼後果?

如果你在stringPtr1上使用“[]”将會發生什麼?結果是未定義的,但是結果不會太好。假設記憶體分布如上圖所示,delete會讀取一些記憶體并把它所讀到的解釋為一個數組容量,接下來就開始多次調用析構函數,卻忽略的以下事實:它處理的記憶體不但不是一個數組,也可能并沒有包含它正忙着釋放的那種類型的對象。

如果你不在stringPtr2上使用“[]”會發生什麼?結果也是未定義的,但是你可以看到這會導緻過少的構造函數被調用。此外,對于像int的内建類型來說結果也是未定義的(有時甚至是有害的),雖然内建類型沒有析構函數。

規則很簡單:如果你在一個new表達式中使用”[]”,你必須在對應的delete表達式中使用”[]”,反之亦然。

當你實作一個包含指向動态配置設定記憶體的指針的類,并且同時提供多個構造函數的時候,你需要将上面的重要規則記在心中,因為你必須當心在對構造函數中對指針成員進行初始化時,new必須使用相同的形式。如果你不這麼做,你又怎麼能知道在析構函數中将使用什麼形式的delete呢?

5. 使用typedef時需要注意new和delete的配對使用

對于傾向于使用typedef的人來說這條規則同樣值得注意,因為這意味着typedef的作者必須指出使用new來建立typedef類型的對象時,使用什麼形式的delete對其進行銷毀。看下面的例子:

1 typedef std::string AddressLines[4]; // a person’s address has 4 lines,
2 
3 // each of which is a string      

因為AddressLines是一個數組,new應該這麼使用:

1 std::string *pal = new AddressLines; // note that “new AddressLines”
2 
3 // returns a string*, just like
4 
5 // “new string[4]” would      

使用delete的形式必須和new相比對:

1 delete pal; // undefined!
2 
3 delete [] pal; // fine      

為了避免這種混淆,不如放棄在數組類型上使用typedef。這很容易,因為标準c++庫(見Item 54)中包含string,vector和模闆,使得對動态配置設定數組的需求幾乎将為0。這裡我們舉個例子,AddressLines可以被定義成由strings組成的vector,也就是類型 vector<string>。

作者:

HarlanC

部落格位址:

http://www.cnblogs.com/harlanc/

個人部落格:

http://www.harlancn.me/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出,

原文連結

如果覺的部落客寫的可以,收到您的贊會是很大的動力,如果您覺的不好,您可以投反對票,但麻煩您留言寫下問題在哪裡,這樣才能共同進步。謝謝!

繼續閱讀