虛函式與虛函式表

2021-07-28 06:27:24 字數 1735 閱讀 7301

虛函式使用中:

為什麼?

1、先看虛函式表:

虛函式表,簡稱虛表。

每乙個有虛函式的類都有這樣乙個東西。實際上記錄了本類中所有虛函式的函式指標,也就是說是個函式指標陣列的起始位置。

虛函式表的結構:它是乙個函式指標表,每乙個表項都指向乙個函式。任何乙個包含至少乙個虛函式的類都會有這樣一張表。需要注意的是vtable只包含虛函式的指標( 函式名是該函式**段的首位址),沒有函式體。實現上是乙個函式指標的陣列。虛函式表既有繼承性又有多型性。每個派生類的vtable繼承了它各個基類的vtable,如果基類vtable中包含某一項,則其派生類的vtable中也將包含同樣的一項(複製陣列的內容),但是兩項的值可能不同。如果派生類

覆寫(override)了該項對應的虛函式,則派生類vtable的該項指向覆寫後的虛函式(更改陣列該項的值),沒有覆寫的話,則沿用基類的值。

每乙個類只有唯一的乙個vtable,不是每個物件都有乙個vtable,恰恰是每個同乙個類的物件都有乙個指標(vfptr),這個指標指向該類的vtable(當然,前提是這個類包含虛函式)。那麼,每個物件只額外增加了乙個指標的大小,一般說來是4位元組。

在類物件的記憶體布局中,首先是該類的

vfptr

指標,然後才是物件資料。

在通過物件指標呼叫乙個虛函式時,編譯器生成的**將先獲取物件類的

vfptr

指標,然後呼叫vtable中對應的項。對於通過物件指標呼叫的情況,在編譯期間無法確定指標指向的是基類物件還是派生類物件,或者是哪個派生類的物件。但是在執行期間執行到呼叫語句時,這一點已經確定,編譯後的呼叫**能夠根據具體物件獲取正確的vtable,呼叫正確的虛函式,從而實現多型性。

2、建構函式不能是虛函式

從儲存空間角度,虛函式對應乙個指向vtable虛函式表的指標,這大家都知道,可是這個指向vtable的指標其實是儲存在物件的記憶體空間的。問題出來了,如果建構函式是虛的,就需要通過 vtable來呼叫,可是物件還沒有例項化,也就是記憶體空間還沒有,怎麼找vtable呢?所以建構函式不能是虛函式。

從使用角度,虛函式主要用於在資訊不全的情況下,能使過載的函式得到對應的呼叫。建構函式本身就是要初始化例項,那使用虛函式也沒有實際意義呀。所以建構函式沒有必要是虛函式。虛函式的作用在於通過父類的指標或者引用來呼叫它的時候能夠變成呼叫子類的那個成員函式。而建構函式是在建立物件時自動呼叫的,不可能通過父類的指標或者引用去呼叫,因此也就規定建構函式不能是虛函式。

從實現上看,vtable在構造函式呼叫後才建立,因而建構函式不可能成為虛函式從實際含義上看,在呼叫建構函式時還不能確定物件的真實型別(因為子類會調父類的建構函式);而且建構函式的作用是提供初始化,在物件生命期只執行一次,不是物件的動態行為,也沒有必要成為虛函式。

3、析構函式可以是虛構函式

使用析構函式時,析構我們往往通過基類的指標來銷毀物件。這時候如果析構函式不是虛函式,就不能正確識別物件型別從而不能正確呼叫析構函式。

4、靜態成員函式不能是虛構函式

虛函式實現多型是通過vfptr這個虛函式表的指標來實現的。從上面你也可以看出實現多型(必須用指向子類物件的基類指標呼叫)是通過被例項化的物件的位址進而找到vfptr[m]實現的,通過物件即使用this指標。

靜態成員函式是屬於某個類的,而不是類的物件的,沒有物件多型也沒有意義。

虛函式與虛函式表

當類中有虛函式時,類的大小會多4個位元組 多出的這4個位元組是乙個位址,指向一張表,裡面儲存了所有虛函式的位址 虛函式表 class base virtual void function 2 virtual void function 3 class sub public base virtual ...

虛函式,虛繼承與虛函式表

c 實現多型機制 模板技術,rtti 技術,虛函式技術,要麼是試圖做到在編譯時決議,要麼試圖做到執行時決議 虛函式 帶有 關鍵字的函式,並且不帶有 標誌的 虛繼承帶有 關鍵字的繼承,基類被稱為虛基類,會在自己物件的例項中產生虛基類指標 虛函式與菱形繼承的問題 當發生繼承時,如果派生類重寫了基類的虛函...

C 虛函式與虛函式表

概念 虛函式 virtual function 是通過一張虛函式表 virtual table 來實現的,簡稱為v table。學習虛函式的作用 理解 c 實現多型的機制 解決了繼承 覆蓋的問題。以下摘抄自 http www.cppblog.com xczhang archive 2008 01 2...