C 「多型」的底層實現,看了之後保你理解

2021-10-06 13:36:45 字數 2247 閱讀 4325

先來看一道題目:

求**中輸出值為多少?

#include

class

text

private

:int _b =1;

};intmain()

很多人在這裡都會犯錯,回答成4,其實結果如下:

這是為什麼呢?

我們通過除錯,借助變數視窗來分析

可以看到,在temp物件中,存在兩個成員,乙個是本事定義的變數int _b,而另乙個變數_vfptr,是乙個"陣列指標」,我們並沒有定義它,而它卻存在於物件中!

重要結論!!!

也就是說,虛函式在類中的儲存是通過乙個指標,該指標指向乙個陣列,對應的虛函式指標分別存放於該陣列中。我們將這個指標稱之為虛表指標

我們大膽猜想,是否和虛函式的定義有關呢?

接著,我們寫乙份完整的多型**。

#include

using

namespace std;

class

base

virtual

void

func2()

void

func3()

private

:int _b =1;

};class

derive

:public base

private

:int _d =2;

};void

function

(base& v)

intmain()

再次借助除錯視窗來分析:

看圖中的那麼陣列指標,該指標指向的陣列的內容在監視視窗也可以看到,注意看下圖中紅色圈起部分

我們發現它們內容是不同的,父型別b中存放的是基類base的兩個成員函式的函式指標,分別為func1於func2,但派生類d中儲存的是從父類繼承來的func2與derive本身重寫的的func1

總結以下幾點

派生類物件d中也有乙個虛表指標。d物件由兩部分構成,一部分是父類繼承下來的成員,另一部分是自身的成員。虛表指標指向的陣列成員也是如此。但又有不同!

基類b物件和派生類d物件虛表是不一樣的,**我們對func1完成了重寫,所以d的虛表中存的是重寫的derive::func1,所以虛函式的重寫也叫作覆蓋,**覆蓋就是指虛表中虛函式的覆蓋。另外func2繼承下來後是虛函式,所以放進了虛表。func3也繼承下來了,但是不是虛函式,所以不會放進虛表。虛函式表本質是乙個存虛函式指標的指標陣列,這個陣列最後麵放了乙個nullptr。

我們再次回顧多型的構成條件:

被呼叫函式必須是虛函式,且該虛函式被子類繼承,同時要被重寫。

必須使用引用或者指標來呼叫虛函式。

這樣就不難理解其中的道理了,因為要完成虛表中的覆蓋,則派生類就必須對虛函式進行重寫

筆者來對以上內容進行總結:

虛函式在類中的存放,就是在這樣的類中,會生成乙個指標,該指標是乙個陣列指標,我們通常將這個陣列成為虛表,虛表內存放的則是這些虛函式的函式指標。

而在實現多型的時候,派生類繼承父類,同樣會有乙個陣列指標,這個指標所指向的陣列(虛表)存放的內容分為兩部分:一是自己本身的虛函式,二是從父類繼承來的。但從父類繼承來的部分虛函式會因為實現多型而被我們重寫,因此進行覆蓋掉。

所產生的結果就是父類的虛表和子類的虛表內容並不相同。而我們通過指標或者引用進行不容物件的呼叫的時候,不同的物件會在虛表內找到相應的函式指標從而執行,和上面例子相匹配的就是b會呼叫父類的func1(),而d會呼叫派生類的func1(),以此達到多型!

多型的底層實現原理

1 首先介紹多型的基本概念 1.1虛函式 在函式前面加上virtual關鍵字,變成虛函式,那麼編譯器在編譯的時候就不能確定函式呼叫了 1.2 多型分為兩類 1.3 靜態多型和動態多型的區別 1.4 使用動態多型需滿足條件 1.5 如何使用動態多型 2.多型的底層實現 父類中寫了乙個虛函式後,內部便有...

C 多型,虛函式作用及底層實現原理

簡述c 虛函式作用及底層實現原理 c 是物件導向程式設計,其包括3項特點 1 資料抽象 介面和實現分離 2 繼承 父類和子類 3 多型 動態繫結 本文討論多型。當父類希望子類重新定義某些函式時,用virtual關鍵字宣告為虛函式。當我們使用乙個基類型別的引用或者指標,呼叫乙個虛函式時就引發動態繫結 ...

C 多型的實現

封裝 繼承 多型,物件導向的三大特性,前兩項理解相對容易,但要理解多型,特別是深入的了解,對於初學者而言可能就會有一定困難了。我一直認為學習oo的最好方法就是結合實踐,封裝 繼承在實際工作中的應用隨處可見,但多型呢?也許未必,可能不經意間用到也不會把它跟 多型 這個詞對應起來。在此拋磚引玉,大家討論...