亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

淺析C++中虛函數(shù)的調(diào)用及對象的內(nèi)部布局

系統(tǒng) 3078 0

在我那篇 《淺析C++中的this指針》 中,我通過分析C++代碼編譯后生成的匯編代碼來分析this指針的實(shí)現(xiàn)方法。這次我依然用分析C++代碼編譯后生成的匯編代碼來說明C++中虛函數(shù)調(diào)用的實(shí)現(xiàn)方法,順便也說明一下C++中的對象內(nèi)部布局。下面所有的匯編代碼都是用VC2005編譯出來的。雖然,不同的編譯器可能會編譯出不同的結(jié)果,對象的內(nèi)部布局也不盡相同;但是,只要是符合C++標(biāo)準(zhǔn)的編譯器,編譯結(jié)果和對象的內(nèi)部布局應(yīng)該是大同小異。
首先,是一個(gè)有著簡單繼承關(guān)系的兩個(gè)類:

class CBase
{
public :
virtual void VFun1() = 0 ;
virtual void VFun2() = 0 ;
void Fun1();
};

// 這里僅僅是為了生成函數(shù)的匯編代碼,因此函數(shù)體為空
void CBase::Fun1()
{
}

class CDerived: public CBase
{
public :
virtual void VFun1();
virtual void VFun2();
void Fun2();
private :
int m_iValue1;
int m_iValue2;
};

// 這里僅僅是為了生成函數(shù)的匯編代碼,因此函數(shù)體為空
void CDerived::VFun1()
{
}

// 這里僅僅是為了生成函數(shù)的匯編代碼,因此函數(shù)體為空
void CDerived::VFun2()
{
}

// 這里是為了分析對象的內(nèi)部布局,因此僅僅是給成員變量賦值
void CDerived::Fun2()
{
m_iValue1
= 13 ;
m_iValue2
= 13 ;
}

現(xiàn)在用下面的代碼來調(diào)用成員函數(shù):

CDerivedderived;

// 用對象調(diào)用虛函數(shù)
derived.VFun1();
derived.VFun2();
// 用對象調(diào)用非虛函數(shù)
derived.Fun1();
derived.Fun2();

// 用指向派生類的基類的指針調(diào)用虛函數(shù),實(shí)現(xiàn)多態(tài)
CBase * pTest = & derived;
pTest
-> VFun1();
pTest
-> VFun2();

下面就是用VC2005編譯上面的代碼后生成的匯編代碼:

CDerivedderived;
0041195Eleaecx,[derived]
00411961 callCDerived::CDerived(411177h)

// 代碼段1
derived.VFun1();
00411966 leaecx,[derived]
00411969 callCDerived::VFun1(411078h)
derived.VFun2();
0041196Eleaecx,[derived]
00411971 callCDerived::VFun2(4111B8h)
derived.Fun1();
00411976 leaecx,[derived]
00411979 callCBase::Fun1(411249h)
derived.Fun2();
0041197Eleaecx,[derived]
00411981 callCDerived::Fun2(4111BDh)

// 代碼段2
CBase * pTest = & derived;
00411986 leaeax,[derived]
00411989 movdwordptr[pTest],eax
pTest
-> VFun1();
0041198Cmoveax,dwordptr[pTest]
// 行1
0041198Fmovedx,dwordptr[eax] // 行2
00411991 movesi,esp
00411993 movecx,dwordptr[pTest]
00411996 moveax,dwordptr[edx] // 行3
00411998 calleax // 行4
0041199Acmpesi,esp
0041199Ccall@ILT
+ 495 (__RTC_CheckEsp)(4111F4h)
pTest
-> VFun2();
004119A1moveax,dwordptr[pTest]
004119A4movedx,dwordptr[eax]
004119A6movesi,esp
004119A8movecx,dwordptr[pTest]
004119ABmoveax,dwordptr[edx
+ 4 ] // 行5
004119AEcalleax
004119B0cmpesi,esp
004119B2call@ILT
+ 495 (__RTC_CheckEsp)(4111F4h)

通過對代碼段1的觀察我們可以發(fā)現(xiàn):通過對象調(diào)用類的虛成員函數(shù)和調(diào)用非虛成員函數(shù)是相同的(對調(diào)用成員函數(shù)的匯編代碼的分析可以看我的那篇 《淺析C++中的this指針》 )。也就是說,用對象是無法實(shí)現(xiàn)多態(tài)的。
下面主要來分析實(shí)現(xiàn)多態(tài)的代碼段2。
行1、將pTest指針指向的地址前2個(gè)字(4個(gè)字節(jié),也就是32位系統(tǒng)中一個(gè)指針的大小)的內(nèi)容當(dāng)成一個(gè)指針放到eax寄存器中
行2、將eax寄存器中的指針的值放入edx寄存器
行3、將dex寄存器中的指針的值放入eax寄存器
行4、調(diào)用eax寄存器指向的函數(shù)
這樣分析似乎對怎樣調(diào)用對象derived的虛函數(shù)VFun1()并不是很清楚。那么我們先來看下面的這張圖:

淺析C++中虛函數(shù)的調(diào)用及對象的內(nèi)部布局

這張圖是一個(gè)假設(shè)的對象derived在內(nèi)存中的內(nèi)部布局圖。指針pTest指向?qū)ο骴erived,而對象derived的前4個(gè)字節(jié)是一個(gè)虛表指針,指向虛函數(shù)表。
看著這張圖再來分析上面的匯編代碼就會清晰很多:
行1、取得虛表指針值放入eax寄存器中
行2、取得虛表指針的值放入edx寄存器中
行3、取得虛表指針指向的地址的值(也就是VFun1)放入eax寄存器中
行4、調(diào)用eax寄存器指向的函數(shù)
行5證明了上面圖中對虛函數(shù)表的假設(shè)。第二個(gè)虛函數(shù)VFun2()的地址就是通過在第一虛函數(shù)VFun1()的地址加4(32位系統(tǒng)中一個(gè)指針的大?。┒玫降?。
通過上面的分析,可以得出C++中虛函數(shù)的調(diào)用方法:首先,取得對象中的虛表指針;然后,通過虛表指針找到相應(yīng)的虛表;最后,通過在虛表內(nèi)的偏移量找到相應(yīng)的函數(shù)來調(diào)用。
下面通過分析類CDerived的非虛成員函數(shù)Fun2()來證明上面圖中虛函數(shù)表指針的存在。

void CDerived::Fun2()
{
004118F0pushebp
004118F1movebp,esp
004118F3subesp,0CCh
004118F9pushebx
004118FApushesi
004118FBpushedi
004118FCpushecx
004118FDleaedi,[ebp
- 0CCh]
00411903 movecx,33h
00411908 moveax,0CCCCCCCCh
0041190Drepstosdwordptres:[edi]
0041190Fpopecx
00411910 movdwordptr[ebp - 8 ],ecx
m_iValue1
= 13 ;
00411913 moveax,dwordptr[ this ] // 行6
00411916 movdwordptr[eax + 4 ],0Dh // 行7
m_iValue2 = 13 ;
0041191Dmoveax,dwordptr[
this ]
00411920 movdwordptr[eax + 8 ],0Dh
}
00411927 popedi
00411928 popesi
00411929 popebx
0041192Amovesp,ebp
0041192Cpopebp
0041192Dret

上面是類CDerived的非虛成員函數(shù)Fun2()的匯編代碼??梢钥吹剑?是將this指向的地址放入eax寄存器,而行7是給this指針指向的地址加4的地址賦值(具體的分析,可以看 《淺析C++中的this指針》 ),而這個(gè)地址里面存放的是類CDerived的第一個(gè)成員變量。我們知道this指針是指向?qū)ο笫椎刂返?,那么為什么要給第一個(gè)成員變量賦值的時(shí)候要向后移動4個(gè)字節(jié)?答案是因?yàn)閷ο蟮那?個(gè)字節(jié)是用來存放虛表指針的。
下面的代碼是 《淺析C++中的this指針》 一文中的不含虛函數(shù)的類的C++代碼和編譯后的匯編代碼:

class CTest
{
public :
void SetValue();

private :
int m_iValue1;
int m_iValue2;
};

void CTest::SetValue()
{
m_iValue1
= 13 ;
m_iValue2
= 13 ;
}

void CTest::SetValue()
{
004117E0pushebp
004117E1movebp,esp
004117E3subesp,0CCh
004117E9pushebx
004117EApushesi
004117EBpushedi
004117ECpushecx
004117EDleaedi,[ebp
- 0CCh]
004117F3movecx,33h
004117F8moveax,0CCCCCCCCh
004117FDrepstosdwordptres:[edi]
004117FFpopecx
00411800 movdwordptr[ebp - 8 ],ecx
m_iValue1
= 13 ;
00411803 moveax,dwordptr[ this ] // 行8
00411806 movdwordptr[eax],0Dh // 行9
m_iValue2 = 13 ;
0041180Cmoveax,dwordptr[
this ]
0041180Fmovdwordptr[eax
+ 4 ],0Dh
}
00411816 popedi
00411817 popesi
00411818 popebx
00411819 movesp,ebp
0041181Bpopebp
0041181Cret

通過行8、行9和行6、行7的比較就可以看出:類CTest的對象前4個(gè)字節(jié)存放的是自己的第一個(gè)成員變量;而類CDerived的對象從第5個(gè)字節(jié)開始才是存放的自己的第一個(gè)成員變量,它的前4個(gè)字節(jié)是用來存放虛表指針的。這再一次證明了上面圖中對象內(nèi)部布局的正確性。

PS:

這篇文章可以說是 《淺析C++中的this指針》 的續(xù)篇,最后我說說我為什么會用這種方法來分析C++,也算是對 《淺析C++中的this指針》 一文中網(wǎng)友評論的回復(fù)吧。
dch4890164建議我看inside the c++ object model;而hacker47卻說了風(fēng)涼話:“孔乙己說:回字有三種寫法,你們知道么?”;最直接的是wengch,直接反問我:“用匯編分析C++.....有意義么?”。而我要說的是,《Inside The C++ Object Model》這本書我看過,確實(shí)是一本非常好的講解C++底層的書。可是由于平時(shí)寫C++代碼的時(shí)候,很少會關(guān)心底層的實(shí)現(xiàn),所以那本書看過之后留下的印象并不深刻。而用匯編代碼來分析C++也是源于一個(gè)很偶然的事件:就是 《淺析C++中的this指針》 一文中提到的可以用一個(gè)類的空指針來調(diào)用成員函數(shù)。我發(fā)現(xiàn)我的C++知識不能解釋那種現(xiàn)象,在Debug代碼的時(shí)候,我轉(zhuǎn)到了匯編代碼中來尋找答案。后來就把我的分析結(jié)果寫成了那篇 《淺析C++中的this指針》 。說實(shí)話,這也是我第一次接觸Windows下的匯編語言,文章中的分析都是邊看資料邊揣摩得出的。也許會有人覺得我這種方法不值一提,但是我卻通過這種方法對C++的底層實(shí)現(xiàn)加深了了解。如果網(wǎng)友們看了覺得有收獲,那我就心滿意足了。呵呵~~

淺析C++中虛函數(shù)的調(diào)用及對象的內(nèi)部布局


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。。?/p>

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 日韩一区在线视频 | 国产福利91精品一区二区 | 深夜在线看片 | 午夜精品成年片色多多 | 干成人| 人人狠狠综合久久亚洲 | 91视频论坛 | 成人在线综合 | 在线观看偷拍视频一区 | 久久精品女人毛片国产 | 黄 色 免费网 站 成 人 | 99久久综合给久久精品 | 成人国产精品视频 | 欧美成人小视频 | 青青草国产三级精品三级 | 亚洲欧洲视频 | 在线成人中文字幕 | 日韩免费观看一级毛片看看 | 一级毛片日韩a欧美视频 | 中文字幕免费在线看线人动作大片 | 国产大片在线观看 | 色哟网站| 国产精品久久久久影院免费 | 狠狠色噜噜狠狠狠狠五月婷 | 特黄女一级毛片 | 精品一区二区三区在线观看视频 | 国产一级aaa全黄毛片 | 性做久久久久久坡多野结衣 | 色婷婷狠狠久久综合五月 | 激情伊人网| 视频一区色眯眯视频在线 | 97夜色| 久久香蕉国产线看观看精品蕉 | 五月婷婷激情网 | 亚洲精品国精品久久99热 | 国产亚洲精品九九久在线观看 | 99国产精品视频免费观看 | 91精品国产色综合久久不卡蜜 | 色多网站免费视频 | 91资源在线 | 色综合视频一区二区观看 |