閱讀635 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C++多態簡析——靜態多態與動態多態

多態:顧名思義,多態就是多種形態,也就是對不同對象發送同一個消息,不同對象會做出不同的響應。

並且多態分為靜態多態和動態多態。

靜態多態就是在係統編譯期間就可以確定程序執行到這裏將要執行哪個函數,例如:函數的重載,對象名加點操作符執行成員函數等,都是靜態多態,其中,重載是在形成符號表的時候,對函數名做了區分,從而確定了程序執行到這裏將要執行哪個函數,對象名加點操作符執行成員函數是通過this指針來調用的。如果你想學習C/C++可以來這個群,首先是三三零,中間是八五九,最後是七六六,裏麵有大量的學習資料可以下載。

函數的重載比較簡單,不再贅述,這裏我們通過一個簡單的例子來看一下對象名加點操作符執行成員函數的靜態多態:

class A

這裏定義了一個A類,有一個成員函數和一個成員,我們將程序的部分匯編代碼截取出來如下圖:

1c5b000071581e5a498a

我們可以看到這裏直接是一個lea指令將a1對象的地址放入寄存器eax中,也就是對象的this指針,然後用call指令就可以跳轉到Set函數,也就是說其匯編代碼在此時就知道應該要去到哪個地方之行哪個函數,這就是靜態多態,也叫編譯時多態

動態多態則是利用虛函數實現了運行時的多態,也就是說在係統編譯的時候並不知道程序將要調用哪一個函數,隻有在運行到這裏的時候才能確定接下來會跳轉到哪一個函數的棧幀。如果你想學習C/C++可以來這個群,首先是三三零,中間是八五九,最後是七六六,裏麵有大量的學習資料可以下載。

在說動態多態之前我們先來看一下什麼是虛函數,虛函數就是在基類中聲明該函數是虛擬的(在函數之前加virtual關鍵字),然後在子類中正式的定義(子類中的該函數的函數名,返回值,函數參數個數,參數類型,全都與基類的所聲明的虛函數相同,此時才能稱為重寫,才符合虛函數,否則就是函數的重載),再定義一個指向基類對象的指針,然後使該指針指向由該基類派生的子類對象,再然後用這個指針來調用改虛函數,就能實現動態多態。

下麵我們通過一個例子來看一下利用虛函數實現的動態多態:

class A

在這裏我們看到,基類A的Get函數聲明為虛函數,在B類中進行了重寫,

然後在main函數中分別用基類的ptr1和指向子類的ptr2進行調用虛函數Get,我們得到了如下圖的輸出:

1b8200049ae80c45d9cf

這說明確實是實現了不同調用,而且是在運行時,那麼虛函數的底層到底是怎麼實現的呢,我們來看一下匯編代碼及其對象模型

1b8200049ae95d30e02e

通過上圖的匯編代碼,我們看到這裏做了一係列的指針解引用處理,最後確定了eax中應該存放的this指針的值,要搞清楚這個必須要搞清楚子類的對象模型。

1b810006021677cde302

用監視窗口查看b1可以看到如上圖所示,這裏的_vfptr是一個虛表指針,它指向一個存放該類對象的所有虛函數的地址的表,我們可以將該表理解為一個函數指針數組,在該數組的最後一個元素,編譯係統會將其置為0,。

對象模型如下圖示:

1b8400048d9672baeb68

其中紅色為A類的成員,黑色為B類對象b1的成員,紫色就是一個虛函數表,存放著存放該類對象的所有虛函數的地址,匯編代碼做了一係列的指針解引用處理就是為了從虛函數表中找到相應的虛函數進行調用,從而實現了動態多態。

最後更新:2017-04-15 20:02:44

  上一篇:go 重要通知 | Window係統高危漏洞修複方案
  下一篇:go Java初學者必知:Java語言的11大特點