要有virtual,及如何理解晚捆綁

下邊要說的是我在語言學習過程中遇到一些不理解的問題,爲什麼要有virtual機制,還有什麼是晚捆綁。我當時學到這的時候可能困惑了一段時間,後來經過繼續學習測試等,得出了自己的結論,僅在這記錄一下。沒有太多的代碼實例,更像是我在囉嗦一些東西。

先說virtual

說道virtual肯定就想到多態,virtual是c++ 實現多態的一種方法,爲什麼要有這樣一套虛機制? 還是那些原則,代碼複用 ”想用不變的代碼去實現不同算法“,大概就是 懶吧。

舉個簡單的例子,(可能我現在在站的高度不夠,理解範圍有限,但這個例子至少是虛機制想要解決的問題中的一種)

有一個方法void test() 來測試不同國家的人說的語言,比如說有 Chinese  Japanese French 等幾個類,類中都有一個speak() 方法

 然後可以考慮這樣寫

 void test(Chinese *p)

{

p->speak();

 void test(Japanese*p)

{

p->speak();

 void test(French *p)

{

p->speak();

這樣可以達到測試每個對象所說語種的目的。 但這樣看起來很不好,這幾個函數,只有參數類型不一樣,內部實現都一樣。那麼想  能不能把參數類型設置成某個特殊的類型,這個特殊的類型可以接收 Chinese  Japanese French 等這些類的對象,然後在調用speak時,根據我實際傳進去的參數類型,再調用每個類中的speak方法。 如果可以的話就能只寫一個函數,實現不同的測試了

void test (special_class *p)

{

p->speak();

}

virtual機制就解決了上述問題,可以弄一個human的基類  然後讓Chinese  Japanese French這幾個類繼承它,  然後語法上允許父類指針指向子類對象

void test(human * p)

{

p->speak();

}

然後問題是 test既然是能處理不同子類對象,那它如何確定該調用哪個子類中的speak()?

這就需要 晚捆綁啊,函數覆蓋啊  虛表啊什麼的那一套來實現,這個不是我困惑的重點,關於虛表可以看看這個

點擊打開鏈接



第二個  關於晚捆綁

學到這的時候我很是困惑,有早捆綁 或者叫 靜態綁定,比如說上邊的test 的參數 只是一個類類型  而不是指針或者引用的話,也就是說它沒有多態,只能處理一種類型的對象,

這時候調用speak 就是靜態綁定,在編譯階段就能完成。   如果有多態  那就叫晚捆綁(動態綁定)  綁定只能在運行時。困惑就在這了,

什麼叫運行時才能綁定啊????



在我的程序運行之前,我的所有代碼不是已經的寫定了麼,編譯之後 已經生成了 那些固定的彙編語句了啊,還有什麼是非得運行之後才確定麼????

說是不知道函數要處理的是哪個具體的子類對象,所以確定不了調用哪個函數, 那既然我調用test的代碼已經寫了

比如     

Chinese  a;

test(a);

那函數要處理的對象不就是Chinese 類型的麼   那就能確定調用的是Chinese 中的speak()了

編譯時就把p->speak(); 換成 call(Chinese中speak的地址) 不就行了麼,還有什麼是在運行之前不能確定的呢?????

?????????????????????????????????????????????????????????????????????????????

當時確實很困惑啊,後來想想 too young too naive 啊

按照上邊的想法,如果只調用test(a);

那是可以 把test函數中的speak調用改成call(Chinese中speak的地址),當程序執行到test(a);這句時 跳到改好的 test()函數的代碼部分  能往下執行,並調用了正確的函數。只要再稍微往後想一點, test()都被改成了 針對對象a的特定版本了, 那還能調用它 來處理其他子類對象麼? 不能處理了 ,那多態跑哪去了?

可見照上述我所想的這種靜態綁定是實現不了多態的。 那多態是如何實現的,是靠動態綁定實現的,在c++編程思想中講的比較明白了,爲了實現多態,編譯器在處理時會隱藏的插入一些內容


簡單說,就是因爲它不知道要處理的對象類型,不能直接去call某個函數的地址,它插入這段代碼,如果是有虛機制的對象,那在對象存儲空間裏能找到v_ptr  也就是虛表指針,這個不管哪個子類的對象只要有virtual   都會有這個指針,然後通過這個指針的偏移處理,在虛函數表中找到要調用的具體版本的函數的地址,再 call     就能實現不管處理什麼類型的子對象,都能找到v_ptr都能通過偏移 找到想要的函數,這就實現了 通用的處理。而這種處理方式,需要在程序運行是,找到傳進來的那個具體對象的存儲空間,然後在裏邊找到v_ptr 然後計算等等, 然後才能得到想要的函數, 也就可以理解 這是發生在運行時。



如果對這一塊有疑惑,推薦去看c++編程思想第二版  第十五章



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章