關于工作和讀書的筆記
[原創文章歡迎轉載,但請保留作者信息]
Justin 于 2009-11-08
其實是說不要在構造/析構函數里調用虛函數。
道理也是不難但也不那么明顯:虛函數擁有虛函數的類就有虛表,虛表可能會引發子類相應虛函數的調用,在這些調用中有可能對某些子類對象成員的訪問。在構造一個子類對象的時候可以分成這幾步:開始構造父類部分 -> 完成父類部分并開始構造子類部分 -> 完成子類部分(完成整個構造工作)析構一個子類對象的時候:開始析構子類部分 -> 子類析構完畢并開始析構父類部分 -> 完成析構父類部分(完成整個析構工作)在構造函數的第一步,子類對象成員還不存在,調用虛函數有可能會訪問不存在的子類成員;哪怕到了第二步,因為子類對象還沒有完全構造完畢,此時調用虛函數也是危險的。事實上在整個構造過程中,該對象都被視作一個父類對象。(不是我說的,是Scott說的)反過來也是同樣道理,在析構函數的第一步,子類成員已經開始銷毀,不能調用虛函數;到了第二步,整個子類部分都沒有了,更不能用虛函數了。而在整個析構過程中,該對象也是被看作是父類對象的。
大師說的是道理,我大概只能聯想。
一個模特的工作是穿上服裝拍照。拍照這個技術活可以看作是這個對象的一個虛函數。在女模的一天之始,美麗動人的她需要先起床(基類構造),習慣裸睡的她還需要到更衣室去穿衣服(子類構造)。然后她對攝影師說“準備好啦”(構造完畢),就開始一天的工作:被拍。到了晚上女模回家(開始析構),先要卸掉身上的束縛(子類析構),最后上床(基類析構),當然,是不是一個人上床不屬于我聯想的范圍。作為拍照這個虛函數來說,女模睡覺的時候是不能做的,這個時候她還沒有擺姿勢的意識;女模起床以后也是不能拍的,因為衣服還沒穿上(OK……我承認這個例子不是很好);穿好衣服,她說“準備好啦”,拍照就可以開始了。同樣道理,最遲你也只能在她晚上說拜拜之前拍完,到回家開始脫衣服睡覺覺之后,你就要等到明天再工作了。
然而問題往往沒有那么簡單明了:很多時候一個構造/析構函數里會調用一些普通的函數,而這些函數又可能間接地調用了虛函數。在這種情況下,傻大粗的編譯器不會報錯。到了災難發生時只有程序員可憐兮兮地抓臭蟲……那么有沒有辦法能夠確保虛函數不會在對象構造/析構過程中被調用呢?別看我,大師開講了:方法之一就是用參數傳遞代替虛函數機制。把可能被構造函數調用的虛函數改成非虛函數,然后讓父類的構造函數將需要的信息/數據通過參數傳遞給子類構造函數。對于析構函數,做法也類似。碼了那么多字,要不,來段代碼消化消化?
Copyright @ Justin.H Powered by: .Text and ASP.NET Theme by: .NET Monster