2012-8-22
一般情況下(特指基類不使用novtable屬性),debug編譯出來的exe,派生類對象的析構,在析構基類部分的時候,對象的虛函數表指針值項會被修改成基類的虛函數表地址,然后再執行基類的析構函數體。
其中高亮的那一行是修改對象的虛函數表指針值為基類虛函數表地址
release下編譯出來的就不是這樣子。以一個小測試程序為例,編譯器做了優化,並沒有在調用基類析構函數時做賦值。但不確定編譯器是否會一直做優化,因為看到了派生類析構函數體實質內容執行之前對虛函數表指針值做了一次賦值,那么為什么在基類析構函數體實質內容執行之前就不需要賦值?編譯器優化的事情就不說了,吃力不討好。下邊圖示調用派生類析構函數時做的事情,包括派生類部分和基類部分。
標紅的地址是派生類析構函數執行前期對虛函數表指針值賦值
注1:novtable我是從這篇文章里看到的:
http://www.cnblogs.com/chio/archive/2007/09/09/887598.html
注2:我使用的工具為VS2005、OllyDbg。
2.不要在構造函數、析構函數里調用虛函數
根據網絡上一些文章的講解,可能出現“Pure Virtual Function Called”的錯誤情況有5種:
1) 基類構造器直接調用虛函數;
2)基類析構器直接調用虛函數;
3)基類構造器間接調用虛函數;
4) 基類析構器間接調用虛函數;
5)通過懸垂指針(dangling pointer,又稱野指針)調用虛函數。
“dangling pointer”導致的“Pure Virtual Function Called”,在vs2005上測試了並沒有出現,因為delete之后,對象的內存值會被修改為某些標志值。可能在其它編譯器上會出現。
一些書籍:
《Effective C++第三版》 建議不要在構造、析構時調用虛函數。
《深度探索C++對象模型》在講解vptr部分時,也分析過在基類構造函數里調用虛函數會出現的情況,它說,這些調用都是“靜態決議”的,無論直接或間接調用虛函數。但實際上可能不是這樣子,這取決於編譯器的實現。VS2005就不是,直接調用會靜態綁定,而間接調用是動態綁定。
如果是小程序的話,這種錯誤很容易發現,如果工程大了,這樣的錯誤非常難找到。所以,書里的建議必須重視。優秀的程序員不是懂得“回”字有多少種寫法,而是有很多寫好工程的好習慣。
3. 網絡上一些不錯的總結:
http://www.artima.com/cppsource/pure_virtual.html (非常完整)
http://www.codeproject.com/Articles/14879/Pure-Virtual-Function-Call (很簡單容易看)
http://blog.csdn.net/kikikind/article/details/2645316 (中文的總結匯總,很簡單容易看懂)
http://www.cnblogs.com/chio/archive/2007/09/09/887598.html (分析匯編,底層而完善)
一些短小的驗證代碼:
http://files.cnblogs.com/cswuyg/%E7%94%B1Pure_Virtual_Function_Called%E8%80%83%E8%99%91%E5%88%B0%E7%9A%84.rar