上一周課程提到,在拷貝賦值中,需要檢測(cè)自我賦值,一來是提高自我賦值時(shí)的效率,二來避免自我賦值中帶有指針時(shí),因?yàn)閮?nèi)存的釋放導(dǎo)致的其它異常。當(dāng)時(shí)有一個(gè)疑問,
自我賦值在實(shí)際代碼編寫中,并不常見,這樣的檢測(cè),多了一層 if 語(yǔ)句判斷,對(duì)于不是自我賦值的調(diào)用,豈不是降低了效率?
很快,第二周的作業(yè)中,又遇到新的問題。

圖片上是自己寫的cp op= 的實(shí)現(xiàn)。嗯,這里按照之前的學(xué)習(xí),做了自我賦值的檢測(cè),但是,比對(duì)老師給出的評(píng)分標(biāo)準(zhǔn),仍然踩到坑了,忘了判斷other中的_leftup(Point*類型)指針是否為空。

很顯然,這里的判斷也是有必要的??赃昕赃甑闹貥?gòu)了自己的代碼:
改過之后,似乎也不怕other._leftup== nullptr了。
可是,這才一個(gè)指針,就需要這樣一堆的 if 做判斷,如果Rectangle里邊的指針再多一點(diǎn),一個(gè)一個(gè)的判斷下來,這效率該多底?。?!
有沒有什么更好的辦法呢?一頭霧水。
說來也巧,翻閱《Effective C++》的時(shí)候,第29條,
Strive for exception-safe code.
書中提到,異常安全函數(shù)(Exception-safe functions)提供三種保證,基本承諾,強(qiáng)烈保證,不拋擲保證(non-throwing)。這里就不具體展開了。同時(shí),書中還提到,有一個(gè)copy and swap設(shè)計(jì)策略,可以提供“強(qiáng)烈保證”。這個(gè),就是這篇文章的重點(diǎn)了。
所謂copy and swap策略,就是從需要修改的對(duì)象拷貝(copy)一份,對(duì)這個(gè)副本進(jìn)行修改,如果有任何異常,原對(duì)象不會(huì)有改變狀態(tài),如果修改成功,就把修改過的副本和原對(duì)象做一個(gè)置換(swap)。
按照這個(gè)策略,繼續(xù)重構(gòu):


上面兩張截圖,保留了重構(gòu)前后的代碼。operator=的實(shí)現(xiàn),只需要短短兩行代碼,other成員的判斷,自我賦值的判斷,通通不需要了。
這下,之前疑惑的幾個(gè)問題,自我賦值判斷的效率問題,代碼冗余,都得到了很好的解決。那么,為什么可以這樣呢?
這里其實(shí)有一個(gè)很重要的細(xì)節(jié),參數(shù)不是pass by reference,而是pass by value。
我們應(yīng)該知道,當(dāng)初老師講到,參數(shù)盡量pass by reference,因?yàn)椴恍枰獎(jiǎng)?chuàng)建一個(gè)副本。也就是說,by value的時(shí)候,會(huì)提供一個(gè)副本。也就是說,一旦進(jìn)入函數(shù)體,實(shí)際上我們已經(jīng)調(diào)用拷貝構(gòu)造,完成了一個(gè)到other副本的拷貝,這就提供了強(qiáng)烈的異常安全保證:如果拷貝失敗,我們不會(huì)進(jìn)入到函數(shù)體內(nèi),那么this指針?biāo)赶虻膬?nèi)容也不會(huì)被改變。
進(jìn)入函數(shù)體以后,other和this進(jìn)行交換,this的數(shù)據(jù)放到臨時(shí)對(duì)象里,這樣在函數(shù)退出時(shí),舊的數(shù)據(jù)自動(dòng)被釋放。
PS.以前只聽說c++很難,現(xiàn)在發(fā)現(xiàn)一個(gè)難點(diǎn)所在了,各種各樣的原則,策略,有些居然是互相矛盾的。在具體應(yīng)用時(shí),我們有更多的選擇,但如何選擇更合適的呢?這才是真正的難點(diǎn)所在。
本人其它筆記: