虛函式=虛函式 虛函式位址表=虛表(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的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式...