C 中虛函式深入剖析

2021-06-06 02:33:48 字數 2811 閱讀 7608

虛函式=虛函式    虛函式位址表=虛表(vtable)

每個類中含有虛函式的物件,編譯器都會為它們指定乙個虛表(其實是乙個函式指標陣列),

儲存在資料區,它由此類所有的物件共用(即靜態的),同時編譯器也會為它(每個類物件)加上乙個成員變數,乙個指向自己虛表的指標(常稱為"vptr"),並存放在物件的首位址上,由此每個類(含有虛函式)分配的物件都有乙個vptr,當我們呼叫虛函式時,實際上是我們通過vptr找到虛表(vtable),再通過偏移來找到真正函式的位址,

其奧妙在於這個vtable以及這種間接呼叫的方法,vtable按照類中虛函式宣告的順序一一填入函式位址,派生類會繼承基類的vtable(當然還會有其它可繼承的成員),當我們在派生類中修改虛函式時,同時派生類中虛表中的內容也隨之被修改,表中相應的元素已經不是基類的函式位址,而是派生類的函式位址

class cshape

;void mytest()

virtual void play() 

virtual void display() 

;class crect : public cshape

;void mytest()  

void display() 

; void mytest()

void display()   }/*

以下是上面那個程式(vtest.cpp)裡for迴圈和迴圈體中的記憶體結構,**的反彙編,和一些注釋,我能證明的只有這些了

以下是棧:

0012ff4c>   00000000             ; int                i;     //(迴圈體內的定義);

0012ff50>   0012ff78             ; cshape*       pshape[0]

0012ff54>   0012ff6c             ;               pshape[1]

0012ff58>   0012ff5c             ;               pshape[2]

0012ff5c>   00426064             ; csquare         asquare;

0012ff60>   00000001             ; b1

0012ff64>   00000002             ; b2

0012ff68>   00000003             ; b3

0012ff6c>   00426048             ; crect          arect;

0012ff70>   00000001             ; b1

0012ff74>   00000002             ; b2

0012ff78>   0042601c             ; cshape        ashape;

0012ff7c>   00000001             ; b1

以下是三個物件vtable的內容(前面是virtual void play()的位址,後面是virtual void display()的位址):

00426064>     37 10 40 00   50 10 40 00

00426048>     37 10 40 00   55 10 40 00

0042601c>     37 10 40 00   5f 10 40 00

以下是**,for迴圈和迴圈體內的反彙編:

004010e9>     jmp short shape.004010f4

004010eb>     mov eax,dword ptr ss:[ebp-34]

004010ee>     add eax,1

004010f1>     mov dword ptr ss:[ebp-34],eax      ;i++

004010f4>     cmp dword ptr ss:[ebp-34],3        ; 迴圈次數的控制,i<3

004010f8>     jge short shape.00401124                  

; 關鍵,定址得到 &pshape

004010fa>     mov ecx,dword ptr ss:[ebp-34]

004010fd>     mov ecx,dword ptr ss:[ebp+ecx*4-30]    

00401101>     mov edx,dword ptr ss:[ebp-34]

00401104>     mov eax,dword ptr ss:[ebp+edx*4-30]

; 關鍵,得到函式表的首位址

00401108>     mov edx,dword ptr ds:[eax]

0040110a>     mov esi,esp

0040110c>     call dword ptr ds:[edx+4]           ; 呼叫虛擬的成員函式

0040110f>     cmp esi,esp

00401111>     call shape.__chkesp                  ; 收拾殘局^_^

; 定址得到 &pshape

00401116>     mov eax,dword ptr ss:[ebp-34]

00401119>     mov ecx,dword ptr ss:[ebp+eax*4-30]

0040111d>     call shape.00401073                  ; 呼叫普通的成員函式

00401122>     jmp short shape.004010eb */

c 中虛函式繼承,虛表剖析

虛表概念 對於有虛函式類,編譯器都會維護一張虛表,物件的前四個位元組就是指向虛表的指標。虛表中存放的是虛函式的位址。虛函式按照其宣告順序存放在虛表中。在派生類中,前面是繼承基類的虛函式,若派生類重寫了基類中的虛函式則替換為重寫後的,派生類自己的虛函式追加在其後。如果派生類繼承了兩個基類,則派生類自己...

C 虛函式表剖析

為了實現c 的多型,c 使用了一種動態繫結的技術。這個技術的核心是虛函式表 下文簡稱虛表 本文介紹虛函式表是如何實現動態繫結的。每個包含了虛函式的類都包含乙個虛表。我們知道,當乙個類 a 繼承另乙個類 b 時,類a會繼承類b的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式...

C 虛函式表剖析

為了實現c 的多型,c 使用了一種動態繫結的技術。這個技術的核心是虛函式表 下文簡稱虛表 本文介紹虛函式表是如何實現動態繫結的。每個包含了虛函式的類都包含乙個虛表。我們知道,當乙個類 a 繼承另乙個類 b 時,類a會繼承類b的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式...