[原創(chuàng)文章歡迎轉(zhuǎn)載,但請(qǐng)保留作者信息]
Justin 于 2009-12-17
C++傳遞對(duì)象的時(shí)候默認(rèn)是傳值的(pass-by-value),而這樣的傳遞自然是昂貴的:這當(dāng)中包含了臨時(shí)對(duì)象的構(gòu)造/析構(gòu),以及臨時(shí)對(duì)象中的對(duì)象的構(gòu)造/析構(gòu),運(yùn)氣背點(diǎn)還可能有對(duì)象中的對(duì)象中的對(duì)象的構(gòu)造/析構(gòu)……(有好的不學(xué),去學(xué)C@#¥%)
相對(duì)于傳“值”,一個(gè)更好的替代方法是傳“const引用”(pass-by-reference-to-const)。
傳值與傳指針的一個(gè)區(qū)別是,通過(guò)傳值傳遞的對(duì)象并不是原來(lái)的對(duì)象,而是一個(gè)復(fù)制品,所以隨便你打它罵它,真身都不會(huì)受到影響。
而通過(guò)傳指針的對(duì)象和原來(lái)的對(duì)象就是同一家伙,改動(dòng)一個(gè)另外一個(gè)也受到相同的影響。而這有時(shí)候并不是我們想要的結(jié)果。
考慮到傳值代價(jià)太高,傳“const引用”就成了一個(gè)很好的替代品。
傳“const引用”的另外一個(gè)好處在于避免了“剝皮問(wèn)題”(slicing problem,侯捷大師的版本是“對(duì)象切割問(wèn)題”,我用這個(gè)中文名字是為了更容易記住:))
書(shū)上的代碼已經(jīng)說(shuō)得很清楚,這里就大概帶過(guò):用傳值方式傳參的函數(shù),如果某參數(shù)的類型是一個(gè)父類對(duì)象,而實(shí)際傳遞的參數(shù)是一個(gè)子類對(duì)象,只有該對(duì)象的父類部分會(huì)被構(gòu)造并傳遞到函數(shù)中,子類部分的成員,作為父類對(duì)象的“皮”,就被血淋淋的剝掉了……
而如果用傳“const引用”方式,就沒(méi)有這種慘無(wú)人道的狀況:本來(lái)父類的指針就可以用來(lái)指向一個(gè)子類對(duì)象,天經(jīng)地義。
但凡有規(guī)矩就有例外,對(duì)于內(nèi)置類型(bulit-in type)對(duì)象以及STL中的迭代器、函數(shù)對(duì)象,Scott還是建議使用傳值方式傳遞,原因是他們本來(lái)就是被設(shè)計(jì)成適合傳值傳遞的。(個(gè)人觀點(diǎn):大師說(shuō):“……it's not unreasonable to choose pass-by-value。”,注意這里有句潛臺(tái)詞:其實(shí)對(duì)以上類型用傳“const引用”方式傳遞也是可以的。)
如果你認(rèn)為上面兩種情況可以用傳值傳遞是因?yàn)樗鼈儯热缯f(shuō)內(nèi)置類型對(duì)象,的大小本來(lái)就小,進(jìn)而得出小數(shù)據(jù)類型就可以用傳值傳遞,就打錯(cuò)特錯(cuò)了。原因見(jiàn)第一段:小對(duì)象的構(gòu)造/析構(gòu)過(guò)程完全可能很恐怖。
再退一步,哪怕某個(gè)類型很小,它的構(gòu)造/析構(gòu)函數(shù)也簡(jiǎn)單到可以忽略不計(jì),我們還是不能以此斷定可以用傳值傳遞這種類型的對(duì)象:因?yàn)榫幾g器往往會(huì)做出一些蠢事。書(shū)中的一個(gè)例子是,對(duì)于一些編譯器可以接受把一個(gè)double類型對(duì)象存入寄存器,但是如果你給它一個(gè)只有一個(gè)double成員的對(duì)象交給它,它卻拒絕將該對(duì)象存入寄存器。(什么事讓編譯器插一手,不是問(wèn)題也有了問(wèn)題……)
最后還有個(gè)理由,雖然某對(duì)象現(xiàn)在很小,可是隨著社會(huì)的發(fā)展人類的進(jìn)步,有可能兩年后它就會(huì)變成一個(gè)龐然大物,到時(shí)候用傳值也會(huì)變得不合適。
因此,還是老實(shí)點(diǎn):除了內(nèi)置類型和STL的迭代器、函數(shù)對(duì)象外,其他的對(duì)象傳遞時(shí),用傳“const引用”代替?zhèn)髦蛋伞?/font>