精品人妻无码一区二区三区软件 ,麻豆亚洲AV成人无码久久精品,成人欧美一区二区三区视频,免费av毛片不卡无码

您現(xiàn)在的位置是:首頁軟件開發(fā)論文

軟件開發(fā)論文繼承情況下直接調(diào)用類成員函數(shù)地址

發(fā)布時間:2013-10-23 13:53:49更新時間:2013-10-23 13:55:14 1

  [摘要]分析在繼承情況下如何取類的成員函數(shù)的地址以及調(diào)用該地址。

  [關(guān)鍵詞]C++成員函數(shù),this指針

  根據(jù)類成員函數(shù)的種類不同,在繼承下如何取成員函數(shù)的地址以及調(diào)用該地址的情況也是有所區(qū)別的。另外還要注意的是多繼承下情況又是如何。類的成員函數(shù)和標準的C函數(shù)不同,類的成員函數(shù)有一個隱藏的指針參數(shù)this,它指向一個類的實例。在VC++中,this一般通過ECX寄存器來傳遞,而普通的成員函數(shù)的參數(shù)被直接壓在堆棧中。this作為參數(shù)和其他普通的參數(shù)有著本質(zhì)的不同,即使一個成員函數(shù)被一個普通函數(shù)的調(diào)用,在標準C++中這個成員函數(shù)和其他的普通函數(shù)的情況不相同,因為沒有thiscall這樣的關(guān)鍵字來保證它像普通參數(shù)一樣正常的調(diào)用。為此,我分別就以下三種情況作了深入的分析。

  一、最簡單的單繼承,非虛擬函數(shù)的情況

  classA{

  public:

  intAf(){return1;}

  };

  classB:publicA

  {

  public:

  intBf(){return2;}

  };

  假如我們建立了B類的一個成員函數(shù)指針。在這個例子中,Af和Bf都是B的成員函數(shù),所以我們的成員函數(shù)指針可以指向Af或者Bf。但是Af需要一個this指針指向B::A(后面我叫它Athis),而Bf需要一個this指針指向B(后面我叫它Bthis)。編譯器保證了A類物理上保存在B類的頭部(即B類的起始地址也就是一個A類的一個實例的起始地址),這意味著Athis==Bthis。

  二、繼承情況下的虛擬函數(shù)

  classA{

  Public:

  virtualintfv(){return11;

  }

  };classB:publicA{

  Public:

  virtualintfv(){return22;}

  };

  現(xiàn)在A和B都定義了虛函數(shù)fv,按C++語法,如果通過指針調(diào)用fv,應該發(fā)生多態(tài)行為。利用下面的代碼:

  DWORDA_fv,B_fv;

  GetMemberFuncAddr_VC6(A_fv,&A::fv);

  GetMemberFuncAddr_VC6(B_fv,&B::fv);

  Ax;By;

  CallMemberFunc(0,A_fv,&x,0);//A::fv

  CallMemberFunc(0,B_fv,&x,0);//B::fv

  CallMemberFunc(0,A_fv,&y,0);//A::fv

  CallMemberFunc(0,B_fv,&y,0);//B::fv

  輸出如下:

  11

  11

  22

  22

  請注意第二行輸出,B_fv取的是&B::fv,但由于傳遞的this指針產(chǎn)生是&x,所以實際上調(diào)用了A::fv。同樣,第三行輸出,取的是基類的函數(shù)地址,但由于實際對象是派生類,最后調(diào)用了派生類的函數(shù)。這說明取得的成員函數(shù)地址在虛擬函數(shù)的情況下仍然保持了正確的行為。源代碼:GetMemberFuncAddr_VC6(B_fv,&B::fv);產(chǎn)生的匯編代碼如下:pushofset@ILT+90(`vcall')(0040105f)。

  原來取B::fv地址的時候,并不是真的就將B::fv的地址傳給了函數(shù),而是傳了一個vcall函數(shù)的地址。顧名思義,vcall當然是虛擬調(diào)用的意思。我們找到地址0040105f,@ILT+90(??_9@$BA@AE):0040105Fjmp`vcall'(00401380)。該地址只是ILT的一個項,直接跳轉(zhuǎn)到真正的vcall函數(shù)(00401380)。找到00401380,就可以看到vcall的代碼'vcall':

  00401380moveax,dwordptr[ecx];//將this指針視為dword類型,并將指向的內(nèi)容(對象的首個//dword)放入eax.

  00401382jmpdwordptr[eax];//跳轉(zhuǎn)到eax所指向的地址。

  代碼執(zhí)行的時候,ecx就是this指針,具體說就是上面對象x或y的地址。而eax就是對象x或y的第一個dword的值。對于有虛擬函數(shù)的類對象,其對象的首地址處總是一個指針,該指針指向一個虛函數(shù)的地址表。上面的對象由于只有一個虛函數(shù),所以虛函數(shù)表也只有一項。因此,直接跳轉(zhuǎn)到eax指向的地址就好。如果有多個虛函數(shù),則eax還要加上一個偏移量,以定位到不同的虛函數(shù)。比如,如果有兩個虛函數(shù),則會有兩個vcall代碼,分別對應不同的虛函數(shù),編譯器根據(jù)取的是哪個虛函數(shù)的地址,則相應的用對應的vcall地址代替。

  三、多繼承情況

  很明顯,現(xiàn)在情況要復雜得多。首先,指定成員函數(shù)的時候可能會碰到?jīng)_突。其次,給定this指針的時候需要經(jīng)過調(diào)整。

  classA{public:

  intAf(){return1;};};

  classB{public:

  intBf(){return2;};};

  classD:publicA,publicB{public:

  intDf(){return4;};};

  現(xiàn)在我們建立一個D類的成員函數(shù)指針。在這種情況下,我們的成員函數(shù)指針可以指向Af、Bf或Df。但是Af需要一個this指針指向D::A,而Bf需要一個this指針指向D::B。這時編譯器不可能把A類和B類都放在D類的頭部。所以,D類的一個成員函數(shù)指針不僅要說明要指明調(diào)用的是哪一個函數(shù),還要指明使用哪一個this指針。編譯器知道A類占用的空間有多大,所以它可以對Athis增加一個delta=sizeof(A)偏移量就可以將Athis指針轉(zhuǎn)換為Bthis指針。

  綜上所述,為了支持一般形式的成員函數(shù)指針,需要至少三條信息:函數(shù)的地址,需要增加到this指針上的delta位移量,和一個虛擬函數(shù)表中的索引。對于VC6.0來說,還需要第四條信息:虛擬函數(shù)表(vtable)的地址。另外,對虛擬繼承可能還要特別處理,而在多繼承的情況下,很多時候成員函數(shù)指針已經(jīng)變成了一個結(jié)構(gòu)體,這時要正確調(diào)用該指針就變得格外困難。當然,解決所有這些問題已經(jīng)超出了這篇文章的范圍。

  參考文獻

  [1]"MemberFunctionPointersandtheFastestPossibleC++Delegates",DonClugston

  [2]《直接調(diào)用類成員函數(shù)地址》,南風.


轉(zhuǎn)載請注明來自:http://www.jinnzone.com/ruanjiankaifalw/22499.html