多型是指同樣的訊息被不同型別的物件接收時導致完全不同的的行為。有虛擬函式的類才能叫多型型別的類,可以從探索虛擬函式是如何實現動態繫結的來了解如何實現多繼承中的多型。
每個類各有一個虛表(虛擬函式表),虛表的內容是由編譯器安排的。c++語言並沒有規定虛擬函式表的內容。派生類的虛表中,基類宣告的虛擬函式對應的指標放在前面,派生類新增的虛擬函式的對應指標放在後面,這樣一個虛擬函式 的指標在基類虛表和派生類虛表中具有相同的位置。每個多型型別的物件中都有一個指 向當前型別的虛表的指標,該指標在建構函式中被賦值。當透過基類的指標或引用呼叫 一 個虛擬函式時,就可以透過虛表指標找到該物件的虛表,進而找到存放該虛擬函式的指標的虛表條目。將該條目中存放的指標讀出後,就可獲得應當被呼叫的函式的入口地址,然後 呼叫該虛擬函式,虛擬函式的動態繫結就是這樣完成的。
如下圖所示:
由上圖可以看到基類Base有f(),g()函式,派生類還有新增的h()函式,那麼這種單繼承的虛擬函式實現動態繫結的方式是這樣的:
從這張超大圖片可以看到,每個Base物件都有一個指向Base的虛表的指標,虛表存放著指向每個函式的指標,這些指標存放著對應函式的地址,這樣透過虛表指標就能找到虛表,透過虛表就能找到函式指標,透過函式指標就能找到函式,這樣虛擬函式的動態繫結就完成了。派生類Base2也一樣。
溫馨提示:執行一個類的建構函式時,首先被執行的是基類的建構函式,因此構造一個派生類的物件時,該物件的虛表指標首先會被指向基類的虛表。只有當基類建構函式執行完後,虛表指標才會被指向派生類的虛表,這就是基類建構函式呼叫虛擬函式時不會呼叫派生類的虛擬函式的原因。
派生類Base3公有繼承了Base,Base2,那麼虛擬函式動態繫結的實現方式是這樣的:
提示:多重繼承時,派生類新增的成員放在第一個表,比如上圖Base3()的虛表的m()指標。
事實上,一個類的虛表中存放的不只是虛擬函式的指標,用於支援執行時型別識別的物件的執行時型別資訊也需要透過虛表來訪問,只有多型型別有虛表,因此只有多型型別支援執行時型別識別。
多型是指同樣的訊息被不同型別的物件接收時導致完全不同的的行為。有虛擬函式的類才能叫多型型別的類,可以從探索虛擬函式是如何實現動態繫結的來了解如何實現多繼承中的多型。
單繼承時虛擬函式動態繫結的實現原理每個類各有一個虛表(虛擬函式表),虛表的內容是由編譯器安排的。c++語言並沒有規定虛擬函式表的內容。派生類的虛表中,基類宣告的虛擬函式對應的指標放在前面,派生類新增的虛擬函式的對應指標放在後面,這樣一個虛擬函式 的指標在基類虛表和派生類虛表中具有相同的位置。每個多型型別的物件中都有一個指 向當前型別的虛表的指標,該指標在建構函式中被賦值。當透過基類的指標或引用呼叫 一 個虛擬函式時,就可以透過虛表指標找到該物件的虛表,進而找到存放該虛擬函式的指標的虛表條目。將該條目中存放的指標讀出後,就可獲得應當被呼叫的函式的入口地址,然後 呼叫該虛擬函式,虛擬函式的動態繫結就是這樣完成的。
如下圖所示:
由上圖可以看到基類Base有f(),g()函式,派生類還有新增的h()函式,那麼這種單繼承的虛擬函式實現動態繫結的方式是這樣的:
從這張超大圖片可以看到,每個Base物件都有一個指向Base的虛表的指標,虛表存放著指向每個函式的指標,這些指標存放著對應函式的地址,這樣透過虛表指標就能找到虛表,透過虛表就能找到函式指標,透過函式指標就能找到函式,這樣虛擬函式的動態繫結就完成了。派生類Base2也一樣。
溫馨提示:執行一個類的建構函式時,首先被執行的是基類的建構函式,因此構造一個派生類的物件時,該物件的虛表指標首先會被指向基類的虛表。只有當基類建構函式執行完後,虛表指標才會被指向派生類的虛表,這就是基類建構函式呼叫虛擬函式時不會呼叫派生類的虛擬函式的原因。
在多繼承時,情況會變得更加複雜,因為在多繼承時,情況會更加複雜,因為每個基類都有各自的虛擬函式,這樣繼承了多個基類的派生類需要多個虛表(或一個虛表分為多段,每個基類的虛表指標指向其中一段的地址。因為有些編譯器把多個虛表連成一個)如圖所示:派生類Base3公有繼承了Base,Base2,那麼虛擬函式動態繫結的實現方式是這樣的:
提示:多重繼承時,派生類新增的成員放在第一個表,比如上圖Base3()的虛表的m()指標。
事實上,一個類的虛表中存放的不只是虛擬函式的指標,用於支援執行時型別識別的物件的執行時型別資訊也需要透過虛表來訪問,只有多型型別有虛表,因此只有多型型別支援執行時型別識別。