午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

VC2008多重繼承下的Virtual Functions:Adjustor Thunk技術(shù)

 legionDataLib 2014-12-16

VC2008多重繼承下的Virtual Functions:Adjustor Thunk技術(shù)

一、多重繼承中Virtual Functions的復(fù)雜性

在多重繼承中支持virtual functions,其復(fù)雜度圍繞在第二個(gè)及后繼的base class身上,以及“必須在執(zhí)行期間調(diào)整this指針”這一點(diǎn)上??聪吕?/font>

#include<iostream>

using namespace std;

class Base1  {

public:

virtual void ShareFunc() = 0;

virtual void Base1Only(){}

virtual Base1Clone( )=0;

virtual ~Base1( ){}

};

class Base2  {

public:

virtual void ShareFunc() = 0;

virtual void Base2Only(){

cout<<this<<endl;

}

virtual Base2Clone( )=0;

virtual ~Base2( ){}

};

class Derived : public Base1public Base2 {

public:

virtual void ShareFunc(){}

virtual DerivedClone( ){return NULL;}

virtual DerivedDerivedOnly( )

{return  this;}

public:

Derived(): m_iValue(0) {}

private:

int m_iValue;

};

int _tmain(int argc_TCHARargv[])

{

Derived obj;

cout<<&obj<<endl;

obj.Base2Only();

Base1pDerive1 = (Base1*)&obj;

pDerive1->ShareFunc();

pDerive1->Base1Only();

Base2pDerive2 = (Base2*)&obj;

pDerive2->ShareFunc();

pDerive2->Base2Only();

return 0;

}

Derived支持virtual functions”的困難度,都落在了Base2 Subobject身上。主要有三個(gè)問(wèn)題:

虛析構(gòu)函數(shù)的調(diào)用virtual destructor(如何通過(guò)第二或后繼之base class的指針或應(yīng)用來(lái)調(diào)用派生類(lèi)的虛函數(shù))。

 被繼承下來(lái)的Base2::Base2Only( )函數(shù)。(通過(guò)一個(gè)指向派生類(lèi)的指針,調(diào)用第二個(gè)base class中一個(gè)繼承而來(lái)的virtual function)。

一組clone( )函數(shù)實(shí)體。( 允許一個(gè)virtual function的返回值類(lèi)型有所變化,可能是base type,也可能是publicly derived type)。

看下面的式子:

Base2* pbase2=new Derived;

Delete pbase2;

編譯器可能參數(shù)的代碼如下:

Derived* temp=new Derived;

Base2* pbase2=temp?temp+sizeof(Base1):0;

//必須首先調(diào)用正確的virtual destructor函數(shù)實(shí)體

//然后實(shí)施delete運(yùn)算符

//pbase2可能需要調(diào)整,已指出完整對(duì)象的起始點(diǎn)

Delete pbase2;

pbase2必須被調(diào)整,以求在一次指向derived對(duì)象的起始處。然而上述的offset加法不能夠在編譯時(shí)期直接設(shè)定(這里的意思是不能夠確定為某一個(gè)常量(eg3,4),當(dāng)可以用一個(gè)變量來(lái)表示),因?yàn)?font style="font-family: 'times new roman';">pbase2所指的真正對(duì)象只有在執(zhí)行期才能確定。

Delete pbase2該調(diào)用操作所連帶的“必要的this指針調(diào)整”操作,必須在執(zhí)行期完成。也就是說(shuō),offset的大小,以及把offset加到this指針上頭的那小段程序代碼,必須由編譯器在某個(gè)地方插入。

二、支持多重繼承Virtual Function的方法

1、CFront方法

CFront編譯器中的方法是將virtual table加大。每一個(gè)virtual table slot不再只是一個(gè)指針,而是一個(gè)聚合體,內(nèi)含可能的offset(偏移量)以及地址(虛函數(shù)地址)。于是virtual function的調(diào)用操作由:

(*pbase2->vptr[1])(pbase2);

改變?yōu)椋?/span>

*pbase2->vptr[1].faddr)

(pbase2+pbase2->vptr[1].offset)

其中faddr內(nèi)含virtual function地址,offset內(nèi)含this指針調(diào)整值。

這個(gè)做法的缺點(diǎn)是:1>改變了每一個(gè)virtual table slot的大小。

                      2>offset的額外存取和加法。

                      3>不能為虛擬繼承中的虛函數(shù)提供同樣的結(jié)局方案。

2、Adjustor Thunk

所謂thunk是一段assembly代碼,用來(lái)完成如下的任務(wù):

以適當(dāng)?shù)?font style="font-family: 'times new roman';">offset值調(diào)整this指針。

跳轉(zhuǎn)到對(duì)應(yīng)的virtual function去。

Thunk技術(shù)允許virtual table slot繼續(xù)內(nèi)含一個(gè)簡(jiǎn)單的指針,因此多重繼承不需要任何空間上的額外負(fù)擔(dān)。Slots中的地址可以直接指向Virtual function,也可以指向一個(gè)相關(guān)的thunk(如果需要調(diào)整this指針的話)。于是,對(duì)于不需要調(diào)整this指針的virtual function,也就不需要承載效率上的額外負(fù)擔(dān)。同時(shí)Thunk技術(shù)也可以為虛擬繼承中的虛函數(shù)提供同樣的解決方案。

三、VC2008 THUNK實(shí)例

下面我們來(lái)看VC2008THUNK的具體實(shí)現(xiàn):

啟動(dòng)調(diào)試,當(dāng)Derived obj執(zhí)行完畢后,我們看到obj的內(nèi)存布局如下圖所示:

VC2008多重繼承下的Virtual Functions:Adjustor Thunk技術(shù) - henry - 風(fēng)云之巔

obj有兩個(gè)虛函數(shù)表,一個(gè)是for 'base1',我們稱(chēng)為base1vtbl;一個(gè)是for 'base2',我們稱(chēng)為base2vtbl。

Derivedsubobject Base1共用一個(gè)虛函數(shù)表,而Base2使用另一個(gè)虛函數(shù)表,到目前為止這和我們想象中的一值。

Derived類(lèi)有一個(gè)非繼承和覆蓋的虛函數(shù)Derived::DerivedOnly( ),它也應(yīng)該在base1vtbl中占有一個(gè)slot,很明顯,在obj的對(duì)象布局中圖中,我們沒(méi)有看到。由于限于語(yǔ)義上的限制,上述的調(diào)試窗口沒(méi)有顯示出DerivedOnlyslot。我們可以到Base1.__vfptr所指向的內(nèi)存區(qū)區(qū)看看,如下圖所示:

VC2008多重繼承下的Virtual Functions:Adjustor Thunk技術(shù) - henry - 風(fēng)云之巔

第一行的前16字節(jié)分別對(duì)應(yīng)base1vtbl的前四項(xiàng),接下來(lái)的四個(gè)字節(jié)0x0041102d對(duì)應(yīng)的就是DerivedOnlyslot。

下面看看Base2.__vfptr所指向的內(nèi)存區(qū):

VC2008多重繼承下的Virtual Functions:Adjustor Thunk技術(shù) - henry - 風(fēng)云之巔

數(shù)據(jù)結(jié)構(gòu)建立起來(lái)后,我們看程序的運(yùn)行。

Base1* pDerive1 = (Base1*)&obj;

0041152C  lea         eax,[ebp-1Ch] 

0041152F  mov         dword ptr [ebp-28h],eax

直接將obj的地址復(fù)制給pDerive1。

1pDerive1->ShareFunc();

pDerive1->ShareFunc();

00411532  mov         eax,dword ptr [ebp-28h] 

00411535  mov         edx,dword ptr [eax] 

00411537  mov         esi,esp 

00411539  mov         ecx,dword ptr [ebp-28h] 

0041153C  mov         eax,dword ptr [edx] 

0041153E  call        eax (跳轉(zhuǎn)到00411e5處)

004111E5  jmp         Derived::ShareFunc (4117A0h) (調(diào)用Derived::ShareFunc()函數(shù))

class Derived : public Base1, public Base2 {

public:

virtual void ShareFunc(){}

004117A0  push        ebp  

004117A1  mov         ebp,esp 

......

00411540  cmp         esi,esp 

00411542  call        @ILT+440(__RTC_CheckEsp) (4111BDh)

由于base1 subobject的起始地址和derived object的起始地址相同,所以通過(guò)pDerive1調(diào)用派生類(lèi)的虛函數(shù)時(shí),沒(méi)有必要調(diào)整this指針,所以這里沒(méi)有使用thunk技術(shù)。 

2pDerive1->Base1Only();

pDerive1->Base1Only();

00411547  mov         eax,dword ptr [ebp-28h] 

0041154A  mov         edx,dword ptr [eax] 

0041154C  mov         esi,esp 

0041154E  mov         ecx,dword ptr [ebp-28h] 

00411551  mov         eax,dword ptr [edx+4] 

00411554  call        eax  (跳轉(zhuǎn)到00411e5處)

00411127  jmp         Base1::Base1Only (411760h)(調(diào)用Base1::ShareFunc()函數(shù))

class Base1  {

public:

virtual void ShareFunc() = 0;

virtual void Base1Only(){}

00411760  push        ebp  

00411761  mov         ebp,esp 

00411763  sub         esp,0CCh 

.......

00411556  cmp         esi,esp 

00411558  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 

通過(guò)base1調(diào)用自己的函數(shù),當(dāng)然用不到thunk技術(shù)。

3Base2* pDerive2 = (Base2*)&obj;

Base2* pDerive2 = (Base2*)&obj;

0041155D  lea         eax,[ebp-1Ch] 

00411560  test        eax,eax (檢測(cè)&obj是否為零)

00411562  je          wmain+92h (411572h) 

00411564  lea         ecx,[ebp-1Ch] 

00411567  add         ecx,4 (調(diào)整地址的值)

0041156A  mov         dword ptr [ebp-114h],ecx 

00411570  jmp         wmain+9Ch (41157Ch) 

00411572  mov         dword ptr [ebp-114h],0 

0041157C  mov         edx,dword ptr [ebp-114h] 

00411582  mov         dword ptr [ebp-34h],edx 

將派生類(lèi)對(duì)象的地址復(fù)制第二個(gè)基類(lèi)的地址時(shí),不僅檢測(cè)地址是否為零,同時(shí)還將地址的值做出相應(yīng)的調(diào)整,使其指向base2 suboubject。

4、pDerive2->ShareFunc();

pDerive2->ShareFunc();

00411585  mov         eax,dword ptr [ebp-34h] 

00411588  mov         edx,dword ptr [eax] 

0041158A  mov         esi,esp 

0041158C  mov         ecx,dword ptr [ebp-34h] 

0041158F  mov         eax,dword ptr [edx] 

00411591  call        eax  (跳轉(zhuǎn)到thunk處)

0041114A  jmp         [thunk]:Derived::ShareFunc`adjustor{4}' (411C40h) 

[thunk]:Derived::ShareFunc`adjustor{4}':

00411C40  sub         ecx,4 (調(diào)整this指針的值,指向derived object)

00411C43  jmp         Derived::ShareFunc (4111E5h) (調(diào)用derived virtual function)

004111E5  jmp         Derived::ShareFunc (4117A0h)

class Derived : public Base1, public Base2 {

public:

virtual void ShareFunc(){}

004117A0  push        ebp  

004117A1  mov         ebp,esp 

                               004117A3  sub         esp,0CCh 

                               ......

00411593  cmp         esi,esp 

00411595  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 

pDerived2指向base2 subobject,它和Derived object的起始地址不一樣,而現(xiàn)在要調(diào)用Derived的函數(shù),相應(yīng)的this指針必須指向Derived object,所以必須調(diào)整this指針,以符合成員函數(shù)的this指針必須指向該成員函數(shù)所屬的對(duì)象。

5、pDerive2->Base2Only();

pDerive2->Base2Only();

0041159A  mov         eax,dword ptr [ebp-34h] 

0041159D  mov         edx,dword ptr [eax] 

0041159F  mov         esi,esp 

004115A1  mov         ecx,dword ptr [ebp-34h] 

004115A4  mov         eax,dword ptr [edx+4] 

004115A7  call        eax  

00411023  jmp         Base2::Base2Only (411690h) 

class Base2  {

public:

virtual void ShareFunc() = 0;

virtual void Base2Only(){}

00411690  push        ebp  

00411691  mov         ebp,esp 

00411693  sub         esp,0CCh 

004115A9  cmp         esi,esp 

004115AB  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 

雖然pDerived2指向base2 subobject,它和Derived object的起始地址不一樣,但現(xiàn)在要調(diào)用的是base2的函數(shù),所以沒(méi)有必要調(diào)整this指針,也就沒(méi)有必要使用thunk技術(shù)。

6、obj.Base2Only();

obj.Base2Only();

004115B0  lea         ecx,[ebp-18h] (調(diào)整this指針,obj地址為[ebp-1ch])

004115B3  call        Base2::Base2Only (411023h) 

00411023  jmp         Base2::Base2Only (411690h) 

class Base2  {

public:

virtual void ShareFunc() = 0;

virtual void Base2Only(){}

00411690  push        ebp  

00411691  mov         ebp,esp

ObjDerived對(duì)象,但調(diào)用的卻是Base2::Base2Only函數(shù),所以有必要調(diào)整this指針,讓它指向Base2 subobject。

7、Base2pb2=pDerive2->Clone();

Base2* pb2=pDerive2->Clone();

004115B8  mov         eax,dword ptr [ebp-34h] 

004115BB  mov         edx,dword ptr [eax] 

004115BD  mov         esi,esp 

004115BF  mov         ecx,dword ptr [ebp-34h] 

004115C2  mov         eax,dword ptr [edx+8] 

004115C5  call        eax  

0041119A  jmp         [thunk]:Derived::Clone`adjustor{4}' (411B90h) 

[thunk]:Derived::Clone`adjustor{4}':

00411B90  sub         ecx,4 

00411B93  jmp         Derived::Clone (411168h) 

00411168  jmp         Derived::Clone (411BA0h) 

Derived::Clone:

00411BA0  push        ebp  

00411BA1  mov         ebp,esp 

00411BA3  sub         esp,0D4h 

00411BA9  push        ebx  

00411BAA  push        esi  

00411BAB  push        edi  

00411BAC  push        ecx  

00411BAD  lea         edi,[ebp-0D4h] 

00411BB3  mov         ecx,35h 

00411BB8  mov         eax,0CCCCCCCCh 

00411BBD  rep stos    dword ptr es:[edi] 

00411BBF  pop         ecx  

00411BC0  mov         dword ptr [ebp-8],ecx 

00411BC3  mov         ecx,dword ptr [this] 

00411BC6  call        Derived::Clone (4110FFh) 

00411BCB  mov         dword ptr [ebp-0D0h],eax 

00411BD1  cmp         dword ptr [ebp-0D0h],0 

00411BD8  je          Derived::Clone+4Bh (411BEBh) 

00411BDA  mov         eax,dword ptr [ebp-0D0h] 

00411BE0  add         eax,4 (調(diào)整返回值Derived地址加4)

00411BE3  mov         dword ptr [ebp-0D4h],eax 

00411BE9  jmp         Derived::Clone+55h (411BF5h) 

00411BEB  mov         dword ptr [ebp-0D4h],0 

00411BF5  mov         eax,dword ptr [ebp-0D4h] 

00411BFB  pop         edi  

00411BFC  pop         esi  

00411BFD  pop         ebx  

00411BFE  add         esp,0D4h 

00411C04  cmp         ebp,esp 

00411C06  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 

00411C0B  mov         esp,ebp 

00411C0D  pop         ebp  

00411C0E  ret           

004115C7  cmp         esi,esp 

004115C9  call        @ILT+440(__RTC_CheckEsp) (4111BDh) 

004115CE  mov         dword ptr [ebp-40h],eax 

小結(jié)

C++成員函數(shù)調(diào)用語(yǔ)意:成員函數(shù)中的this指針必須指向該成員函數(shù)所屬的對(duì)象。單繼承中虛函數(shù)的調(diào)用會(huì)始終保持這種語(yǔ)意,因?yàn)榕缮?lèi)對(duì)象的地址和基類(lèi)子對(duì)象的地址一致,無(wú)論是通過(guò)派生類(lèi)調(diào)用基類(lèi)的函數(shù)還是通過(guò)基類(lèi)調(diào)用派生類(lèi)的函數(shù),他們的this指針都只有一個(gè),那就是派生類(lèi)對(duì)象的起始地址。但是在多重繼承中,第二以及其之后的基類(lèi)子對(duì)象的起始地址和派生類(lèi)對(duì)象的啟示地址存在偏差,所以在通過(guò)基類(lèi)指針調(diào)用派生類(lèi)函數(shù)時(shí)(多態(tài)),由于其不滿足成員函數(shù)調(diào)用語(yǔ)意,所以必須調(diào)整this指針。同理,通過(guò)派生類(lèi)對(duì)象調(diào)用基類(lèi)虛函數(shù)時(shí)(繼承來(lái)的虛函數(shù)),也必須調(diào)整this指針。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多