C++輔導(dǎo):內(nèi)存分配和釋放

字號(hào):

內(nèi)存分配和釋放幾乎是所有程序的基本要求,同時(shí)也是最容易出現(xiàn)問(wèn)題的地方之一。通過(guò)遵循幾條簡(jiǎn)單的規(guī)則,你可以避免很多常見(jiàn)的內(nèi)存分配問(wèn)題。
     原則1 用new、delete取代malloc、calloc、realloc和free
     malloc、calloc、realloc和free是C語(yǔ)言的用法,它們不會(huì)理會(huì)對(duì)象的存在與否,更不會(huì)去調(diào)用構(gòu)造和析構(gòu)函數(shù),所以在C++中經(jīng)常會(huì)引起麻煩。如果混用這些函數(shù)會(huì)造成更大的麻煩。比如要防止用malloc分配,用delete釋放,這些都需要花費(fèi)格外的精力。
     原則2 new、delete和new[]、delete[]要成對(duì)使用
     調(diào)用new所包含的動(dòng)作:
     從系統(tǒng)堆中申請(qǐng)恰當(dāng)?shù)囊粔K內(nèi)存。
     若是對(duì)象,調(diào)用相應(yīng)類(lèi)的構(gòu)造函數(shù),并以剛申請(qǐng)的內(nèi)存地址作為this參數(shù)。
     調(diào)用new[n]所包含的動(dòng)作:
     從系統(tǒng)堆中申請(qǐng)可容納n個(gè)對(duì)象外加一個(gè)整型的一塊內(nèi)存;
     將n記錄在額外的哪個(gè)整型內(nèi)存中。(其位置依賴(lài)于不同的實(shí)現(xiàn),有的在申請(qǐng)的內(nèi)存塊開(kāi)頭,有的在末尾);
     調(diào)用n次構(gòu)造函數(shù)初始化這塊內(nèi)存中的n個(gè)連續(xù)對(duì)象。
     調(diào)用delete所包含的動(dòng)作:
     若是對(duì)象,調(diào)用相應(yīng)類(lèi)的析構(gòu)函數(shù)(在delete參數(shù)所指的內(nèi)存處);
     該該內(nèi)存返回系統(tǒng)堆。
     調(diào)用delete[]所包含的動(dòng)作:
     從new[]記錄n的地方將n值找出;
     調(diào)用n次析構(gòu)函數(shù)析構(gòu)這快內(nèi)存中的n個(gè)連續(xù)對(duì)象;
     將這一整快內(nèi)存(包括記錄n的整數(shù))歸還系統(tǒng)堆。
     可以看出,分配和釋放單個(gè)元素與數(shù)組的操作不同,這就是為什么一定要成對(duì)使用這些操作符的原因。
     原則3 確保所有new出來(lái)的東西適時(shí)被delete掉
     不需要的內(nèi)存不能及時(shí)被釋放(回系統(tǒng))就是大家常聽(tīng)到的內(nèi)存泄露(memory leak)。狹義的內(nèi)存泄露是指分配的內(nèi)存不再使用,從更廣義上說(shuō),沒(méi)有及時(shí)釋放也是內(nèi)存泄露,只是程度較輕而已。內(nèi)存泄露不管有多少,只要運(yùn)行的時(shí)間夠長(zhǎng),最終都回耗盡所有的內(nèi)存。
     內(nèi)存泄露的問(wèn)題極難查找,因?yàn)楫?dāng)出現(xiàn)問(wèn)題時(shí),內(nèi)存已然耗盡,此時(shí)CPU正在運(yùn)行什么程序,什么程序就崩潰(哪怕是系統(tǒng)程序)。可以看出,崩潰時(shí)報(bào)告的錯(cuò)誤信息與引起問(wèn)題的代碼毫無(wú)關(guān)系。另外內(nèi)存耗盡的時(shí)間也是不確定的,且一般是一個(gè)較長(zhǎng)的時(shí)間(幾天或幾個(gè)星期),這就更增加了再現(xiàn)和定位問(wèn)題的難度。
     原則4 自定義類(lèi)的new/delete操作符一定要符合原操作符號(hào)的行為規(guī)范。比如,帶同樣的參數(shù),new要返回一個(gè)內(nèi)存指針,若沒(méi)有足夠的內(nèi)存供分配則返回NULL,等等。另外,自定義類(lèi)的new操作符一定要自定義類(lèi)的delete操作符。
     calss Aboutmemory
     {
     public:
     void* operate new(size);
     void operate delete(void*);
     void* operate new[](size);
     void operate delete[](void*);
     }
     原則5 當(dāng)所有的內(nèi)存被釋放后,指針應(yīng)該有一個(gè)合理的值,除非該指針本身要消失,否則應(yīng)置為NULL。
     另外,千萬(wàn)不要忘了給字符串結(jié)束符“\0”申請(qǐng)一個(gè)空間!
     PS:所有涉及資源管理的代碼應(yīng)使用諸如自動(dòng)指針(auto pointer)或引用計(jì)數(shù)之類(lèi)的技術(shù)。
     1.自動(dòng)指針在其構(gòu)造函數(shù)中接受或自己申請(qǐng)一個(gè)系統(tǒng)堆內(nèi)存,并在析構(gòu)函數(shù)中釋放該內(nèi)存。
     2.這樣,當(dāng)該自動(dòng)指針?biāo)诘膶?duì)象消失或所在的函數(shù)退出時(shí),自動(dòng)指針的析構(gòu)函數(shù)會(huì)被系統(tǒng)自動(dòng)調(diào)用(局部變量要被系統(tǒng)自動(dòng)釋放),從而最終自動(dòng)釋放其所管轄的系統(tǒng)堆內(nèi)存。實(shí)際上,這是把一個(gè)系統(tǒng)堆內(nèi)存的生命周期限制在一貫生命周期有限的局部變量上。
     3.如果異常發(fā)生,C++的異常機(jī)制保證所有已構(gòu)造成功的局部變量會(huì)被自動(dòng)析構(gòu)。
     4.自動(dòng)指針可以通過(guò)拷貝或賦值傳遞其擁有的內(nèi)存:它們把所管轄的系統(tǒng)堆內(nèi)存從源自動(dòng)指針轉(zhuǎn)移到目的自動(dòng)指針,此后源自動(dòng)指針不在擁有該系統(tǒng)堆內(nèi)存的管轄權(quán)(析構(gòu)時(shí)釋放的將是一個(gè)空指針)。通過(guò)自動(dòng)指針的拷貝和賦值,可以使系統(tǒng)堆內(nèi)存能生存于申請(qǐng)者之外而不需要申請(qǐng)者釋放之。
     5.可以看出,編程者不需要關(guān)心何時(shí),由誰(shuí)來(lái)釋放申請(qǐng)的內(nèi)存,他只需要指定下一步誰(shuí)(函數(shù),對(duì)象)要擁有該內(nèi)存,并做簡(jiǎn)單的轉(zhuǎn)移(拷貝或賦值)即可。擁有者內(nèi)部的自動(dòng)指針將完成適時(shí)的回收動(dòng)作。