關於虛函式的背景知識
用virtual關鍵字申明的函式叫做虛函式,虛函式肯定是類的成員函式。
存在虛函式的類都有乙個一維的虛函式表叫做虛表。類的物件有乙個指向虛表開始的虛指標。虛表是和類對應的,虛表指標是和物件對應的。
多型性是乙個介面多種實現,是物件導向的核心。分為類的多型性和函式的多型性。
多型用虛函式來實現,結合動態繫結。
純虛函式是虛函式再加上= 0。並且該函式只有宣告,沒有實現。
抽象類是指包括至少乙個純虛函式的類。
那虛函式是如何執行的呢?
cpp**
class base
} class derive : public base
} void main()
編譯器在編譯的時候,發現base類中有虛函式,此時編譯器會為每個包含虛函式的類建立乙個虛表(即vtable),該表是乙個一維陣列,在這個陣列中存放每個虛函式的位址。由於base類和derive類都包含了乙個虛函式func(),編譯器會為這兩個類都建立乙個虛表,(即使子類裡面沒有virtual函式,但是其父類裡面有,所以子類中也有了)
那麼如何定位虛表呢?編譯器另外還為每個類的物件提供了乙個虛表指標(即vptr),這個指標指向了物件所屬類的虛表。在程式執行時,根據物件的型別去初始化vptr,從而讓vptr正確的指向所屬類的虛表。所以在呼叫虛函式時,就能夠找到正確的函式。
對於上述程式,由於pb實際指向的物件型別是derive,因此vptr指向的derive類的vtable,當呼叫pb->func()時,根據虛表中的函式位址找到的就是derive類的func()函式。
正是由於每個物件呼叫的虛函式都是通過虛表指標來索引的,也就決定了虛表指標的正確初始化是非常重要的。換句話說,在虛表指標沒有正確初始化之前,我們不能夠去呼叫虛函式。那麼虛表指標在什麼時候,或者說在什麼地方初始化呢?
答案是在建構函式中進行虛表的建立和虛表指標的初始化。
還記得建構函式的呼叫順序嗎,在構造子類物件時,要先呼叫父類的建構函式,此時編譯器只「看到了」父類,並不知道後面是否後還有繼承者,它初始化父類物件的虛表指標,該虛表指標指向父類的虛表。當執行子類的建構函式時,子類物件的虛表指標被初始化,指向自身的虛表。對於以上的例子,當derive類的d物件構造完畢後,其內部的虛表指標也就被初始化為指向derive類的虛表。在型別轉換後,呼叫pb->func(),由於pb實際指向的是derive類的物件,該物件內部的虛表指標指向的是derive類的虛表,因此最終呼叫的是derive類的func()函式。
要注意:對於虛函式呼叫來說,每乙個物件內部都有乙個虛表指標,該虛表指標被初始化為本類的虛表。所以在程式中,不管你的物件型別如何轉換,但該物件內部的虛表指標是固定的,所以呢,才能實現動態的物件函式呼叫,這就是c++多型性實現的原理。
總結(基類有虛函式):
每乙個類都有虛表。
虛表可以繼承,如果子類沒有重寫虛函式,那麼子類虛表中仍然會有該函式的位址,只不過這個位址指向的是基類的虛函式實現。如果基類有3個虛函式,那麼基類的虛表中就有三項(虛函式位址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函式,那麼虛表中的位址就會改變,指向自身的虛函式實現。如果派生類有自己的虛函式,那麼虛表中就會新增該項。
派生類的虛表中虛函式位址的排列順序和基類的虛表中虛函式位址排列順序相同。
虛函式 虛指標和虛表
關於虛函式的背景知識 用virtual關鍵字申明的函式叫做虛函式,虛函式肯定是類的成員函式。存在虛函式的類都有乙個一維的虛函式表叫做虛表。類的物件有乙個指向虛表開始的虛指標。虛表是和類對應的,虛表指標是和物件對應的。多型性是乙個介面多種實現,是物件導向的核心。分為類的多型性和函式的多型性。多型用虛函...
虛函式,虛表,虛表指標
分享一篇文章,詳細解釋了為什麼通過基類指標指向基類物件或派生類物件,就可以呼叫相應類的虛函式。自 一 概述 為了實現c 的多型,c 使用了一種動態繫結的技術。這個技術的核心是虛函式表 下文簡稱虛表 本文介紹虛函式表是如何實現動態繫結的。二 類的虛表 每個包含了虛函式的類都包含乙個虛表。我們知道,當乙...
虛函式,虛指標和虛表詳解
關於虛函式的背景知識 1.用virtual關鍵字申明的函式叫做虛函式,虛函式肯定是類的成員函式。2.存在虛函式的類都有乙個一維的虛函式表叫做虛表。每乙個類的物件都有乙個指向虛表開始的虛指標。虛表是和類對應的,虛表指標是和物件對應的。3.多型性是乙個介面多種實現,是物件導向的核心。分為編譯多型性和執行...