單繼承時虛函數動態綁定的實現原理
每個類各有一個虛表(虛函數表),虛表的內容是由編譯器安排的。c++語言并沒有規定虛函數表的內容。派生類的虛表中,基類聲明的虛函數對應的指針放在前面,派生類新增的虛函數的對應指針放在后面,這樣一個虛函數的指針在基類虛表和派生類虛表中具有相同的位置。每個多態類型的對象中都有一個指向當前類型的虛表的指針,該指針在構造函數中被賦值。當通過基類的指針或引用調用一個虛函數時,就可以通過虛表指針找到該對象的虛表,進而找到存放該虛函數的指針的虛表條目。將該條目中存放的指針讀出后,就可獲得應當被調用的函數的入口地址,然后調用該虛函數,虛函數的動態綁定就是這樣完成的。
如下圖所示:
由上圖可以看到基類Base有f(),g()函數,派生類還有新增的h()函數,那么這種單繼承的虛函數實現動態綁定的方式是這樣的:
從這張超大圖片可以看到,每個Base對象都有一個指向Base的虛表的指針,虛表存放著指向每個函數的指針,這些指針存放著對應函數的地址,這樣通過虛表指針就能找到虛表,通過虛表就能找到函數指針,通過函數指針就能找到函數,這樣虛函數的動態綁定就完成了。派生類Base2也一樣。
在多繼承時,情況會變得更加復雜,因為在多繼承時,情況會更加復雜,因為每個基類都有各自的虛函數,這樣繼承了多個基類的派生類需要多個虛表(或一個虛表分為多段,每個基類的虛表指針指向其中一段的地址。因為有些編譯器把多個虛表連成一個)如圖所示:
派生類Base3公有繼承了Base,Base2,那么虛函數動態綁定的實現方式是這樣的:
事實上,一個類的虛表中存放的不只是虛函數的指針,用于支持運行時類型識別的對象的運行時類型信息也需要通過虛表來訪問,只有多態類型有虛表,因此只有多態類型支持運行時類型識別。