我們主要就是想從C++的對象模型裡找到後一個答案。前一個答案在軟件工程中是毫無疑義的,面向對象的優越性要比C語言裡一堆數據結構+和一堆可能與它們相關的函數,可讀性、可用性好很好,對開發大型軟件工程,需要幾百人開發一個項目來說,C++好太多了。看看JAVA或者python程序員們,他們為什麼可以一直站在巨人的肩膀上,想完成任何一個功能都超級方便的調用大師們以前寫好的package/API,借用各種設計模式,應用級別程序員們可以非常EASY的使用復雜的設計,一些只有高級C程序員才能掌握的東東。當然,JAVA的很多特性也導致不適應核心服務器的開發,比如它的垃圾回收機制。
OK,閒話少敘,在看對象模型前,先看幾個C++與C語言的典型不同之處。
1、自然是類的定義了,最大的改變就是類把數據結構與方法捆到一起了,可讀性上提升巨大。對成員變量和成員方法,有5種類型:static member, nonstatic member, static function, nonstatic function, virtual function.
2、繼承,這裡很有許多細節了,核心解決問題就是動態綁定,也就是virtual關鍵字。virtual出現的唯一原因就是為了解決繼承機制,否則struct裡引入方法就足夠了,class出現就是為了這。virtual關鍵字解決了子類實例和父類實例的一些特殊關系,考慮以下場景:軟件工程中,很喜歡每個模塊專注於自己的事,盡量忽略與自己無關的實現,這樣,很可能會用一個父類指針,該指針太可能指向多種不同的子類了,但是現在,使用這個抽象父類指針的模塊不想關注細節,當它調用對象的某個方法時,到底是調用父類的方法還是子類的方法呢?動態綁定這個特性就是,開發者可以決定這一點,當你用virtual關鍵字申明父類方法時,如果子類重定義了該方法,如果這個指針實際指向的是某子類對象,那麼調用的方法一定是該子類方法的實現。
舉個例子吧,就像什麼析構函數總喜歡寫成virtual?這個例子應該容易說明virtual的玩法。一段簡單的代碼:
[cpp]那麼以上,C++對象模型是怎麼做到的呢?畫張象征性的圖吧。先定義一個類,再看看它的內存布局:
[cpp]我們生成一個Father對象,看看它的的內存布局是啥樣的(同志們,這只是近似存儲布局圖,沒有把編譯和運行的差別放上去,下篇再講這個):
這裡大家明白了吧?即使一個Child對象在編譯時被賦為Father類型,但是實際調用時,virtual方法會被單獨的拎出來,在vtbl中指向實際的實現,所以,該對象在delete時會調用Child的析構函數,而如果你像上面例子那樣,析構方法不使用virtual,將會用到上圖中的最後一個指針,指向類成員函數裡,這樣就不是執行期綁定了。
剩下的static成員(還有所有的正常成員函數),都是與對象實例無關的內存布局。這樣,其實如果不使用virtual,C++比之C並沒有增加成本,盡可放心使用。