delete this 的使用及注意事項(xiàng)
- this對(duì)象是必須是用 new操作符分配的(而不是用new[]产阱,也不是用placement new苹粟,也不是局部對(duì)象,也不是global對(duì)象)耻台;
- delete this后诚欠,不能訪問該對(duì)象任何的成員變量及虛函數(shù)(delete this回收的是數(shù)據(jù),這包括對(duì)象的數(shù)據(jù)成員以及vtable草丧,不包括函數(shù)代碼)狸臣;
- delete this后,不能再訪問this指針昌执。換句話說烛亦,你不能去檢查它、將它和其他指針比較懂拾、和 NULL比較煤禽、打印它、轉(zhuǎn)換它岖赋,以及其它的任何事情檬果;
個(gè)人認(rèn)為保證以上禁忌列表基本手段可以包括:
- 將析構(gòu)函數(shù)私有化(如果有子類,則protected化贾节,保證子類能夠正確繼承)--以保證對(duì)象必須使用new在堆上分配內(nèi)存汁汗;
- 提供(可以在僅僅在基類中)Destroy(void)函數(shù),里面僅有一句delete this--以保證第三方能夠?qū)⒎峙涞膬?nèi)存回收栗涂;
比如你new了一個(gè)對(duì)象實(shí)例知牌,然后使用者需要調(diào)用對(duì)象里的一個(gè)服務(wù)函數(shù),想服務(wù)函數(shù)結(jié)束后就不再需要對(duì)象實(shí)例了斤程,也就是說可以刪除了角寸,那么我們就可以通過在函數(shù)最后調(diào)用delete this將自己刪除,也就是傳說中的“自殺”忿墅。
但為什么能自殺呢扁藕,我個(gè)人簡單的理解就是類成員函數(shù)是真實(shí)存在的,不管你實(shí)例是否存在疚脐,只不過如果沒有實(shí)例的話亿柑,類成員函數(shù)中的this指針就不存在了,所以這也是平時(shí)我們不能隨意調(diào)用類成員函數(shù)的原因棍弄,那么當(dāng)我們調(diào)用delete this時(shí)望薄,他刪除了對(duì)象自己,但我們的程序依然跑在那個(gè)成員函數(shù)的代碼中呼畸,只要delete this后面沒有什么數(shù)據(jù)是依賴于this指針的話也就不會(huì)出現(xiàn)問題了痕支。
當(dāng)然這里有個(gè)例外的地方,就是不能在析構(gòu)函數(shù)里調(diào)用delete this蛮原,否則就會(huì)進(jìn)入死循環(huán)了卧须。。。花嘶。笋籽。(這個(gè)你懂的,我不說了)
delete 執(zhí)行了哪些步驟察绷?
在對(duì)類指針使用delete時(shí)干签,實(shí)際發(fā)生了兩個(gè)步驟。
A:先是調(diào)用該類的析構(gòu)函數(shù)拆撼,以做數(shù)據(jù)成員的釋放工作容劳,以及一些finish code,這一切由程序員自己定義闸度。
B:然后再調(diào)用operator delete(void*)釋放該對(duì)象實(shí)例的內(nèi)存數(shù)據(jù)竭贩。這是一個(gè)對(duì)象在消亡之前的所做的最后動(dòng)作。一般不要override這個(gè)函數(shù)莺禁,如果要留量,務(wù)必記住最后調(diào)用系統(tǒng)的::operator delete真正釋放該對(duì)象所占用的內(nèi)存。
一般來說哟冬,內(nèi)存釋放釋放的只能是數(shù)據(jù)段的內(nèi)容(包括堆和棧楼熄,但釋放棧上的內(nèi)存由系統(tǒng)進(jìn)行),而代碼段的內(nèi)存浩峡,除一些病毒攻擊等非正常強(qiáng)行改寫手段外可岂,在內(nèi)存中是永遠(yuǎn)不會(huì)釋放/改變的,直到程序結(jié)束翰灾,因此在內(nèi)存釋放后也是可以訪問的缕粹。所以,一般所謂的釋放內(nèi)存delete操作纸淮,是在數(shù)據(jù)段進(jìn)行的釋放平斩。
delete原語看起來會(huì)是如下的樣子:
p->~object();
object::operator delete(p);
因?yàn)榇a段的內(nèi)存是不會(huì)被釋放的,因此無論對(duì)象p的內(nèi)存有沒有釋放咽块,這兩個(gè)語句都會(huì)執(zhí)行绘面,不會(huì)因?yàn)閜沒有指向任何存在的對(duì)象而報(bào)錯(cuò),只是在最后執(zhí)行到::operator delete的時(shí)候侈沪,才會(huì)在執(zhí)行_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse))的時(shí)候報(bào)錯(cuò)飒货。
在類的成員函數(shù)中調(diào)用delete this
在類的成員函數(shù)中能不能調(diào)用delete this?答案是肯定的峭竣,能調(diào)用,而且很多老一點(diǎn)的庫都有這種代碼晃虫。假設(shè)這個(gè)成員函數(shù)名字叫release皆撩,而delete this就在這個(gè)release方法中被調(diào)用,那么這個(gè)對(duì)象在調(diào)用release方法后,還能進(jìn)行其他操作扛吞,如調(diào)用該對(duì)象的其他方法么呻惕?答案仍然是肯定 的,調(diào)用release之后還能調(diào)用其他的方法滥比,但是有個(gè)前提:被調(diào)用的方法不涉及這個(gè)對(duì)象的數(shù)據(jù)成員和虛函數(shù)亚脆。說到這里,相信大家都能明白為什么會(huì)這樣 了盲泛。
根本原因在于delete操作符的功能和類對(duì)象的內(nèi)存模型濒持。當(dāng)一個(gè)類對(duì)象聲明時(shí),系統(tǒng)會(huì)為其分配內(nèi)存空間寺滚。在類對(duì)象的內(nèi)存空間中柑营,只有數(shù)據(jù)成員和虛函數(shù)表指針,并不包含代碼內(nèi)容村视,類的成員函數(shù)單獨(dú)放在代碼段中官套。在調(diào)用成員函數(shù)時(shí),隱含傳遞一個(gè)this指針蚁孔,讓成員函數(shù)知道當(dāng)前是哪個(gè)對(duì)象在調(diào)用它奶赔。當(dāng) 調(diào)用delete this時(shí),類對(duì)象的內(nèi)存空間被釋放杠氢。在delete this之后進(jìn)行的其他任何函數(shù)調(diào)用站刑,只要不涉及到this指針的內(nèi)容,都能夠正常運(yùn)行修然。一旦涉及到this指針笛钝,如操作數(shù)據(jù)成員,調(diào)用虛函數(shù)等愕宋,就會(huì)出現(xiàn)不可預(yù)期的問題玻靡。
為什么是不可預(yù)期的問題?delete this之后不是釋放了類對(duì)象的內(nèi)存空間了么中贝,那么這段內(nèi)存應(yīng)該已經(jīng)還給系統(tǒng)囤捻,不再屬于這個(gè)進(jìn)程。照這個(gè)邏輯來看邻寿,應(yīng)該發(fā)生指針錯(cuò)誤蝎土,無訪問權(quán)限之類的令系統(tǒng)崩潰的問題才對(duì)啊绣否?這個(gè)問題牽涉到操作系統(tǒng)的內(nèi)存管理策略誊涯。delete this釋放了類對(duì)象的內(nèi)存空間,但是內(nèi)存空間卻并不是馬上被回收到系統(tǒng)中蒜撮,可能是緩沖或者其他什么原因暴构,導(dǎo)致這段內(nèi)存空間暫時(shí)并沒有被系統(tǒng)收回跪呈。此時(shí)這段內(nèi)存是可以訪問的,你可以加上100取逾,加上200耗绿,但是其中的值卻是不確定的。當(dāng)你獲取數(shù)據(jù)成員砾隅,可能得到的是一串很長的未初始化的隨機(jī)數(shù)误阻;訪問虛函數(shù)表,指針無效的可能性非常高晴埂,造成系統(tǒng)崩潰究反。
大致明白在成員函數(shù)中調(diào)用delete this會(huì)發(fā)生什么之后,再來看看另一個(gè)問題邑时,如果在類的析構(gòu)函數(shù)中調(diào)用delete this奴紧,會(huì)發(fā)生什么?實(shí)驗(yàn)告訴我們晶丘,會(huì)導(dǎo)致堆棧溢出黍氮。原因很簡單,delete的本質(zhì)是“為將被釋放的內(nèi)存調(diào)用一個(gè)或多個(gè)析構(gòu)函數(shù)浅浮,然后沫浆,釋放內(nèi)存” (來自effective c++)。顯然滚秩,delete this會(huì)去調(diào)用本對(duì)象的析構(gòu)函數(shù)专执,而析構(gòu)函數(shù)中又調(diào)用delete this,形成無限遞歸郁油,造成堆棧溢出本股,系統(tǒng)崩潰。
上面是某大牛的分析桐腌,而在實(shí)際的運(yùn)行過程中使用delele this確實(shí)會(huì)直接出現(xiàn)錯(cuò)誤拄显。這是因?yàn)椋涸诔蓡T函數(shù)中調(diào)用delete this,首先會(huì)調(diào)用類的析構(gòu)函數(shù)案站,this指針已刪除躬审,會(huì)出現(xiàn)指針錯(cuò)誤。
下面是在XCode中使用delete this出現(xiàn)的錯(cuò)誤:
malloc: *** error for object 0xbffffa18: pointer being freed was not allocated
//注意0xbffffa18即為this的地址
*** set a breakpoint in malloc_error_break to debug
而在VS2010中使用delete this是直接導(dǎo)致 Debug Assertion Failed蟆盐!
具體的描述是:invalid null pointer
總結(jié):在成員函數(shù)中調(diào)用delete this承边,會(huì)導(dǎo)致指針錯(cuò)誤,而在析構(gòu)函數(shù)中調(diào)用delete this石挂,出導(dǎo)致死循環(huán)博助,造成堆棧溢出。
PS:this是類中成員函數(shù)具有的一個(gè)附加的隱含形參痹愚,即指向該類對(duì)象的一個(gè)指針富岳,它與調(diào)用成員函數(shù)的對(duì)象綁定在一起罗心。同時(shí)1.在普通的非const成員函數(shù)中:this的類型是一個(gè)指向類類型的const指針,可以改變this指向的值城瞎,但是不能改變this所保存的地址;2.在const成員函數(shù)中疾瓮,this的類型是一個(gè)指向const類類型對(duì)象的const指針脖镀,既不能改變this所指向的對(duì)象,也不能改變this所保存的地址狼电。
注意:
- 成員函數(shù)中不能定義this形參蜒灰,而是由編譯器隱含地定義,但是可以在成員函數(shù)中顯示使用this形參肩碟,不過也不是必須這么做强窖。如果對(duì)類成員的引用沒有限定,編譯器會(huì)將這種引用處理成通過this指針的引用削祈。
- 有一種情況下必須顯式使用this:當(dāng)我們需要將一個(gè)對(duì)象作為整體引用而不是引用對(duì)象的一個(gè)成員時(shí)翅溺。
- 從const成員函數(shù)返回*this:不能從const成員函數(shù)返回指向類對(duì)象的普通引用。const成員函數(shù)只能返回*this作為一個(gè)const引用髓抑。