既然提到了指標,那就讓我們用記憶體位址來證明一下吧。
為了證明,我們必須要取到成員函式的首位址。利用下面的函式取得成員函式的位址
template
dst_type pointer_cast
(src_type src)
呼叫上面函式的方法:
void* p1 = pointer_cast(&類名::成員方法名);
#include
"stdio.h"
#include
"string.h"
#include
template
dst_type pointer_cast
(src_type src)
class
a void
funca
()};
class
b:public a
void
funcb
()};
intmain
()
實驗環境:gcc version 7.3.0。ubuntu18.04
gdb裡的操作步驟1,先用【info line 22】命令去得到a類的成員函式funca的位址,22是成員函式funca所在的行號。
gdb裡的操作步驟2,因為p1是指向a類的成員函式funca的指標,所以【p p1】的結果就是a類的成員函式funca的位址。
位址偏移量是啥?
當執行程式時,先從硬碟把程式載入到記憶體中,這個程式就成為了乙個執行中的程序,作業系統會給程序分配虛擬記憶體空間,為了能夠呼叫到函式,必須知道函式在的虛擬記憶體空間的位址。這個位址呼叫側怎麼知道的呢,在編譯階段,編譯器自動把被呼叫函式相對於程序首位址的偏移量算出來了,告訴了呼叫測,所以呼叫側才能找到被呼叫函式的位址。
()指標p1指向的類a的func成員函式;
指標p2指向的類b的func成員函式;
但是從gdb的結果來看,他們指向的位址都是0x1,也就說明他們沒有正確的指向類的成員函式。
那麼類的成員函式的位址在**呢?看下面的gdb截圖
通過【p x】檢視物件x,發現x裡面多了個_vptr的東西。這個東西就是最開始說的二級指標(指標陣列)。
步驟2:【p *(void**)0x555555755d48】,先把_vptr指標轉成void型的二級指標,然後再用【*】取得位址裡的內容,發現位址類存放的就是類a的成員函式func的位址0x555555554cc4。
問題來了,如何得到第二個呢?因為64位系統指標所佔8個位元組,所以(_vptr + 8)就是第二個虛函式的位址。
當有如下**:用父類的指標指向子類的物件(多型的最終目的:面對抽象類程式設計),然後呼叫子類和父類完全相同的函式(必須是虛函式)。讓人迷惑,到底呼叫的是哪個。
a* m = new b(1,2,3,4);//b是a的子類
m->func();
m->func1();
用父類的指標呼叫虛函式時,先去它指向的記憶體中(子類所占用的記憶體)找_vptr,然後從_vptr裡找函式的位址。非虛函式的位址不在_vptr裡。
步驟1:【p *m】,發現m是類a的物件
步驟2:【set print object on】,含義是顯示物件真正的型別
步驟3:【p *m】,發現m原來不是類a的物件,而是類b的物件。
步驟4:檢視_vptr裡第乙個指標,發現指向的是b的func;加8後得到第二個指標,發現發現指向的是b的func1.
c 多型的內幕
既然提到了指標,那就讓我們用記憶體位址來證明一下吧。為了證明,我們必須要取到成員函式的首位址。利用下面的函式取得成員函式的位址 templatedst type pointer cast src type src 呼叫上面函式的方法 void p1 pointer cast 類名 成員方法名 inc...
C 多型 繼承多型
什麼是多型?個人理解為 在程式語言繼承關係中,子類能替代父類,表現出不同的行為。換句話說 在繼承關係中,乙個類被例項化被其子類替代,子類中有父類的虛方法重寫,或者有父類同名方法 new 呼叫相同方法時候,將表現出子類或者父類中不同行為 老闆,上 static void main string arg...
C 多型 靜態多型與動態多型
多型 顧名思義,多型就是多種形態,也就是對不同物件傳送同乙個訊息,不同物件會做出不同的響應。並且多型分為靜態多型和動態多型。靜態多型就是在系統編譯期間就可以確定程式執行到這裡將要執行哪個函式,例如 函式的過載,物件名加點操作符執行成員函式等,都是靜態多型,其中,過載是在形成符號表的時候,對函式名做了...