[原創(chuàng)文章歡迎轉(zhuǎn)載,但請保留作者信息]
Justin 于 2009-12-19
在認(rèn)真學(xué)習(xí)第20課后,大師有點(diǎn)擔(dān)心學(xué)生馬上變成傳引用(pass-by-reference)的粉絲。為了辯證的看待問題,下一課現(xiàn)在開講……
初看題目,第21條玉律說的也是一個(gè)很簡單的情況:如果函數(shù)需要返回一個(gè)對象,就不要僅僅返回引用,因?yàn)榉祷氐囊糜锌赡芤呀?jīng)指向一個(gè)不存在的對象了。
沒錯(cuò),不過細(xì)讀下去發(fā)現(xiàn)還是有一些的細(xì)節(jié)自己原來并沒有考慮到。
如果函數(shù)返回的是值(對象),那么就難免要進(jìn)行創(chuàng)建對象操作(構(gòu)造/析構(gòu))來存儲返回的結(jié)果;如果是返回引用,就可以避免這額外的對象創(chuàng)建,節(jié)省了資源和時(shí)間。
看來,似乎返回引用要優(yōu)于返回整個(gè)對象。
是這樣么?大師繼續(xù)揭開更本質(zhì)的一些東西:
如果一個(gè)函數(shù)可能返回一個(gè)對原來不存在的對象的引用,那么函數(shù)就要自己去創(chuàng)建這個(gè)對象:要么在棧上(stack)要么在堆上(heap)。
-
第一種情況中,函數(shù)中定義了局部對象,然后返回對該對象的引用。對象在函數(shù)結(jié)束后自動銷毀,引用指向無效的地址。
OK,很簡單。
-
第二種情況,函數(shù)使用new動態(tài)創(chuàng)建了一個(gè)對象,然后返回對該對象的引用。粗看沒有問題,因?yàn)檫@個(gè)返回的引用還是有效的。
但是細(xì)想就會發(fā)現(xiàn):我們能確保這個(gè)對象被正確的收回(delete)嗎?
書中舉了一個(gè)很好的例子:一個(gè)*運(yùn)算符函數(shù),接受兩個(gè)乘法運(yùn)算數(shù),返回一個(gè)積。
如果在函數(shù)中動態(tài)創(chuàng)建一個(gè)對象來存儲乘積,并返回對這個(gè)新對象的引用,那么下面的計(jì)算就會帶來內(nèi)存泄漏:
??
Y=A*B*C
因?yàn)樵谶@個(gè)“連續(xù)”乘法中,有兩個(gè)“乘積對象”被創(chuàng)建,但是我們丟失了第一次乘法創(chuàng)建的對象的指針。
所以這樣的做法是不妥的。
也許大師有被問過:那么對于第一種情況我們可不可以返回一個(gè)靜態(tài)(static)對象的引用?書中用了同樣的例子來回答:NO。
?? if (A*B == C*D) {//..}
如果返回靜態(tài)對象的引用,上面的判斷語句永遠(yuǎn)得到true值,因?yàn)椤?=”號兩邊的運(yùn)算結(jié)果是指向同一塊數(shù)據(jù)的引用。
不知道是不是后面又有刨根問題的學(xué)生追問:那我能不能用一個(gè)靜態(tài)對象的數(shù)組來存放不同此運(yùn)算的結(jié)果?大師懶得舉例子了,我猜想也沒必要:這樣的方案帶來的副作用及其開銷本身就已經(jīng)大于原來要解決的問題了吧?
最后總結(jié)本課中心思想:
- 不要嘗試在函數(shù)中返回對局部對象(存儲于棧)的引用,也不要對動態(tài)創(chuàng)建的對象(存儲于堆)做同樣的蠢事,而如果真有打算、非常渴望、十分想要返回對靜態(tài)對象的引用,在這么做之前也要考慮清楚會不會有上面例子中的情況出現(xiàn)。
- 至少,返回對象不會有上面列出的種種錯(cuò)誤危險(xiǎn),僅僅是有可能帶來創(chuàng)建額外對象的開銷而已,而這個(gè)開銷的可能還有可能被排除,如果你用的編譯器足夠聰明的話。
下課!
謝~謝~老~師~