摘 要:虛函數(shù)是面向?qū)ο蟮腃++語言中的一個非常重要的概念。它充分體現(xiàn)了面向?qū)ο笏枷胫械睦^承和多態(tài)性這兩大特性,動態(tài)綁定是C++中實現(xiàn)多態(tài)的一個重要途徑,虛函數(shù)是動態(tài)綁定的基礎(chǔ)。通過深入分析VC++編譯器實現(xiàn)虛函數(shù)的匯編代碼。給出在繼承下的實現(xiàn)模型,并結(jié)合實例,在匯編語言層次研究了多態(tài)性的實現(xiàn)機理,揭示了虛函數(shù)和動態(tài)綁定的本質(zhì)。
關(guān)鍵詞:虛函數(shù);多態(tài)性;綁定;構(gòu)造函數(shù)
中圖分類號:TP312文獻標識碼:A
文章編號:1004-373X(2010)04-154-03
Study of Virtual Function in C++
XU Qifeng,HU Yong,WANG Yucheng
(Xuzhou Air Force College,Xuzhou,221000,China)
Abstract:Virtual Function is an important concept of object oriented language C++.It presents the Inheritance and Polymorphism of the idea in object oriented language,dynamic binding is an important approach to realize polymorphism in C++ and virtual function is its foundation,this paper investigates the realization mechanism of virtual function in assemble language in VC++.According to inheritance relationship,the realize model of polymorphism is studied by example and in assemble language,and reveals the essence of virtual function and dynamic binding .
Keywords:virtual function;polymorphism;bind;constructor
0 引 言
多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計語言繼數(shù)據(jù)抽象和繼承之后的第三個基本特征[1,2],在C++語言中,多態(tài)性是用虛函數(shù)來實現(xiàn)的[3,4],如果某類中的一個成員函數(shù)被說明為虛函數(shù),該類被稱為多態(tài)類,這就意味著該成員函數(shù)在派生類中可以有不同的實現(xiàn),當用一個基類指針或引用指向一個繼承類對象調(diào)用虛函數(shù)的時候,實際調(diào)用的是繼承類版本的虛函數(shù)。
綁定是指把函數(shù)定義體與函數(shù)調(diào)用相聯(lián)接的過程,按照綁定所進行的階段不同,可分為兩種不同的綁定方法:靜態(tài)綁定和動態(tài)綁定[5,6],靜態(tài)綁定是傳統(tǒng)的過程式程序設(shè)計語言所使用的方法,即函數(shù)定義體和函數(shù)調(diào)用間的聯(lián)接發(fā)生在編譯連接階段,這種聯(lián)編又稱早期綁定,因為綁定過程是在程序開始運行之前完成的。靜態(tài)綁定的優(yōu)點是有效性,即速度快,所需內(nèi)存小,易實現(xiàn)代碼優(yōu)化,主要缺點是缺乏靈活性。與靜態(tài)綁定相對應(yīng)的是動態(tài)綁定,動態(tài)綁定意味著函數(shù)調(diào)用與函數(shù)定義體的聯(lián)接是在程序運行是進行的,即在編譯時,編譯器不能確定某一對象實體與具體那個函數(shù)調(diào)用相聯(lián)接,必須在運行時刻根據(jù)上下文關(guān)系來確定[7]。動態(tài)綁定也叫晚期綁定,動態(tài)綁定以效率性來換取靈活性。在C++語言中,當使用基類指針或引用調(diào)用虛函數(shù)采用的就是動態(tài)綁定的方式[8],即根據(jù)基類指針或引用的動態(tài)類型(指針或引用所指向或引用對象的類型)選擇相應(yīng)的虛函數(shù)版本,而不是根據(jù)基類指針或引用的靜態(tài)類型(聲明指針或引用時所使用的類型)來選擇函數(shù)版本,從而給程序帶來了多態(tài)性[9-11]。
1 虛函數(shù)的實現(xiàn)機理
下面從一個具體的程序來分析虛函數(shù)的實現(xiàn)機理,程序如下,在Visual C++ 6.0編譯器下運行。
#include
using namespace std;
class CBase
{
public:
int Base_x;
virtual void vFun1();
virtual void vFun2();
void Fun3();
};
void CBase::vFun1()
{
cout << \"CBase::vFun1()\"< } void CBase::vFun2() { cout << \"CBase::vFun2()\"< } void CBase::Fun3() { cout << \"CBase::Fun3()\"< } class CDerived:public CBase { int Derived_x; int Derived_y; void vFun2(); }; void CDerived::vFun2() { cout << \"CDerived::vFun2()\"< } void main() { CBase * pBase; CDerived Derived; pBase = Derived; pBase->vFun1(); pBase->vFun2(); pBase->Fun3(); } 該程序定義了兩個類:CBase 類和他的派生類CDerived類。CBase類中有兩個虛函數(shù) vFun1()和vFun2(),另外還有一個非虛函數(shù)Fun3(),在CDerived類中改寫了vFun2()的定義,最后,在main()函數(shù)中,用基類Cbase定義了一個指針pBase,然后將定義好的Cderived類的一個對象的地址賦給pBase,即指針pBase的靜態(tài)類型是Cbase,動態(tài)類型是Cderived,接下來用pBase對vFun1(),vFun2(),F(xiàn)un3()分別進行調(diào)用,輸出結(jié)果如下: CBase::vFun1() CDerived::vFun2() CBase::Fun3() 下面通過分析編譯器生成的匯編代碼來分析虛函數(shù)的實現(xiàn)機理,首先分析對Fun3()函數(shù)調(diào)用生成的匯編代碼: pBase->Fun3(); 這條語句對應(yīng)的匯編代碼如下: mov ecx,DWORD PTR _pBaseMYM[ebp] call ?Fun3@CBase@@QAEXXZ; CBase::Fun3 從上面的代碼可以看出,因為Fun3()是非虛擬的成員函數(shù),不論pBase 指針所指向?qū)ο蟮念愋褪鞘裁?,對Fun3()的調(diào)用是依據(jù)pBase的靜態(tài)類型Cbase 來綁定相應(yīng)的函數(shù)定義體的,所以pBase->Fun3()最終轉(zhuǎn)化成對CBase::Fun3()的調(diào)用。這就是前面所提到的靜態(tài)綁定。下面在看看編譯器為pBase->vFun2()這條語句所生成的匯編代碼: mov eax,DWORD PTR _pBaseMYM[ebp] mov edx,DWORD PTR [eax] mov esi,esp mov ecx,DWORD PTR _pBaseMYM[ebp] call DWORD PTR [edx+4] cmp esi,esp call __chkesp _pBaseMYM[ebp]就是pBase的實際值,即Derived對象的地址。對vFun2()的調(diào)用最終轉(zhuǎn)化為call DWORD PTR [edx+4],從代碼可以看出,edx的值就是Derived對象的地址。為了弄清楚[edx+4]代表什么,需要分析Derived對象的內(nèi)存空間的布局情況,首先看一下Derived對象的構(gòu)造函數(shù)的代碼,下面摘錄了其中的關(guān)鍵代碼: mov DWORD PTR _thisMYM[ebp],ecx mov ecx,DWORD PTR _thisMYM[ebp] call ??0CBase@@QAE@XZ ; CBase::CBase mov eax,DWORD PTR _thisMYM[ebp] mov DWORD PTR [eax],OFFSET FLAT:??_7CDerived@@6B@ ; CDerived::′vftable′ mov eax,DWORD PTR _thisMYM[ebp] Cbase類的構(gòu)造函數(shù)相關(guān)代碼如下所示: mov DWORD PTR _thisMYM[ebp],ecx mov eax,DWORD PTR _thisMYM[ebp] mov DWORD PTR [eax],OFFSET FLAT:??_7CBase@@6B@ ; CBase::′vftable′ mov eax,DWORD PTR _thisMYM[ebp] 從上面的代碼可以看出,編譯器生成的vftable地址即vptr插入到存放對象內(nèi)存區(qū)的首地址(即this指針指向的地方)的代碼。所以說,帶有虛函數(shù)的對象在構(gòu)造時,涉及到vftable,而vftable在CONST區(qū)定義,代碼如下所示: CONST SEGMENT ??_7CDerived@@6B@ DDFLAT:?vFun1@CBase@@UAEXXZ; CDerived::′vftable′ DDFLAT:?vFun2@CDerived@@EAEXXZ CONST ENDS 2 結(jié) 語 從上面的代碼可以看出,vftable其實就是一個容納虛函數(shù)的地址的表格[12],Cderived的虛函數(shù)表格里面有從基類繼承而來的虛函數(shù)vFun1()和改寫了定義的vFun2()的地址。Cderived的構(gòu)造函數(shù)調(diào)用了基類Cbase的構(gòu)造函數(shù),CBase的構(gòu)造函數(shù)也有初始化虛函數(shù)指針的過程,但由于Cderived的構(gòu)造函數(shù)初始化虛函數(shù)指針在調(diào)用CBase的構(gòu)造函數(shù)之后,因此最終的虛函數(shù)表容納的是派生類的虛函數(shù)的地址,結(jié)合前面編譯器為pBase->vFun2()語句所生成的匯編代碼,可以得出如下結(jié)論: (1) 編譯器必須為多態(tài)類的每個對象增加一個vptr指針,由多態(tài)類的構(gòu)造函數(shù)負責vptr的初始化; (2) 每個多態(tài)類至少增加一個vftable表到程序的數(shù)據(jù)區(qū),vftable就是一張指向虛擬函數(shù)的函數(shù)指針表; (3) 每個虛函數(shù)的調(diào)用被編譯器轉(zhuǎn)化通過查找vftable來定位函數(shù)地址(需要額外的機器指令),如pBase->vFun2()將會轉(zhuǎn)化為(*pBase->vptr[2])(pBase)。 以上結(jié)論是針對visual C++ 6.0編譯器而言的,對于其他編譯器,具體實現(xiàn)雖不完全相同,但都大同小異。如gnu C++編譯器就把指向vtable的指針放在對象尾部而不是頭部。 參考文獻 [1]藍雯飛.C++語言中的面向?qū)ο筇卣魈接慬J].計算機工程與應(yīng)用,2000,36(9):91-92. [2]夏承遺,董玉濤,趙德新,等.C++中虛函數(shù)的實現(xiàn)機制[J].天津理工學(xué)院學(xué)報,2004,20(3):65-67. [3]Bjarne Stroustrup.The C++ Programming Language[M].Special Edition(影印版).北京:高等教育出版社,2001. [4]張昀.C++中的多態(tài)性研究[J].教育技術(shù)導(dǎo)刊,2009,18(2):65-66. [5]Terrence W Pratt.程序設(shè)計語言:設(shè)計與實現(xiàn)[M].傅育熙,譯.北京:電子工業(yè)出版社,2001. [6]趙紅超,方金云,唐志敏.C++的動態(tài)多態(tài)和靜態(tài)多態(tài)[J].計算機工程,2005,31(20):72-73,87. [7]Bjarne Stroustrup.C++的設(shè)計與演化[M].裘宗燕,譯.北京:機械工業(yè)出版社,2002. [8]藍雯飛,陸際光.C++面向?qū)ο蟪绦蛟O(shè)計中的多態(tài)性研究[J].計算機工程與應(yīng)用,2000,36(8):97-98. [9]Stanley B Lippman.Inside the C++ Object Model[M].侯捷,譯.武漢:華中科技大學(xué)出版社,2001. [10]張亞鵬.關(guān)于C++中虛函數(shù)的幾個問題[J].赤峰學(xué)院學(xué)報:自然科學(xué)版,2006(2):132-133. [11]和力,吳麗賢.關(guān)于C++虛函數(shù)底層實現(xiàn)機制的研究與分析[J].計算機工程與設(shè)計,2008,29(10):2 705-2 707. [12]Scott Mayers.More Effective C++ [M].2nd Edition.Addi-sion Wesley Lonman Inc.,1996. [13]袁亞麗,肖桂云.C++中虛函數(shù)的實現(xiàn)技術(shù)研究[J].河北北方學(xué)院學(xué)報:自然科學(xué)版,2006,22(5):67-69,75. [14]藍雯飛.C++中的多態(tài)性及其應(yīng)用[J].計算機時代,1998(7):15-16.