真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

c++中的虛函數(shù)

本篇內(nèi)容主要講解“c++中的虛函數(shù)”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“c++中的虛函數(shù)”吧!

創(chuàng)新互聯(lián)是一家專業(yè)的成都網(wǎng)站建設(shè)公司,我們專注網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)、網(wǎng)絡(luò)營(yíng)銷(xiāo)、企業(yè)網(wǎng)站建設(shè),友情鏈接1元廣告為企業(yè)客戶提供一站式建站解決方案,能帶給客戶新的互聯(lián)網(wǎng)理念。從網(wǎng)站結(jié)構(gòu)的規(guī)劃UI設(shè)計(jì)到用戶體驗(yàn)提高,創(chuàng)新互聯(lián)力求做到盡善盡美。

匯編語(yǔ)言是難讀的,特別是對(duì)一些沒(méi)有匯編基礎(chǔ)的朋友,因此,本文將匯編翻譯成相應(yīng)的C語(yǔ)言,以方便讀者分析問(wèn)題。

1. 代碼

   為了方便表述問(wèn)題,本文選取只有虛函數(shù)的兩個(gè)類,當(dāng)然,還有它的構(gòu)造函數(shù),如下:

[cpp] view
plaincopyprint?

  1. class Base      

  2. {     

  3.  public:     

  4.     virtual void f() { }     

  5.     virtual void g() { }     

  6. };     

  7. class Derive : public Base     

  8. {     

  9.   public:  

  10.     virtual void f() {}  

  11. };     

  12. int main()     

  13. {     

  14.   Derive d;     

  15.   Base *pb;  

  16.   pb = &d;   

  17.   pb->f();  

  18.   return 0;  

  19. }   

2. 兩個(gè)類的虛函數(shù)表(vtable)

使用g++ –Wall –S test.cpp命令,可以將上述的C++代碼生成它相應(yīng)的匯編代碼。

[cpp] view
plaincopyprint?

  1. _ZTV4Base:  

  2.     .long   0    

  3.     .long   _ZTI4Base  

  4.     .long   _ZN4Base1fEv  

  5.     .long   _ZN4Base1gEv  

  6.     .weak   _ZTS6Derive  

  7.     .section    .rodata._ZTS6Derive,"aG",@progbits,_ZTS6Derive,comdat  

  8.     .type   _ZTS6Derive, @object  

  9.     .size   _ZTS6Derive, 8   

_ZTV4Base是一個(gè)數(shù)據(jù)符號(hào),它的命名規(guī)則是根據(jù)g++的內(nèi)部規(guī)則來(lái)命名的,如果你想查看它真正表示C++的符號(hào)名,可使用c++filt命令來(lái)轉(zhuǎn)換,例如:

[lyt@t468 ~]$ c++filt _ZTV4Base 
vtable for Base

_ZTV4Base符號(hào)(或者變量)可看作為一個(gè)數(shù)組,它的第一項(xiàng)是0,第二項(xiàng)_ZIT4Base是關(guān)于Base的類型信息,這與typeid有關(guān)。為方便討論,我們略去此二項(xiàng)數(shù)據(jù)。 因此Base類的vtable的結(jié)構(gòu),翻譯成相應(yīng)的C語(yǔ)言定義如下:

[cpp] view
plaincopyprint?

  1. unsigned long Base_vtable[] = {  

  2.     &Base::f(),  

  3.     &Base::g(),  

  4. };  

而Derive的更是類似,只有稍為有點(diǎn)不同:

[cpp] view
plaincopyprint?

  1. _ZTV6Derive:  

  2.     .long   0    

  3.     .long   _ZTI6Derive  

  4.     .long   _ZN6Derive1fEv  

  5.     .long   _ZN4Base1gEv  

  6.     .weak   _ZTV4Base  

  7.     .section    .rodata._ZTV4Base,"aG",@progbits,_ZTV4Base,comdat  

  8.     .align 8  

  9.     .type   _ZTV4Base, @object  

  10.     .size   _ZTV4Base, 16   

相應(yīng)的C語(yǔ)言定義如下:

[cpp] view
plaincopyprint?

  1. unsigned long Derive_vtable[] = {  

  2.     &Derive::f(),  

  3.     &Base::g(),  

  4. };  

從上面兩個(gè)類的vtable可以看到,Derive的vtable中的第一項(xiàng)重寫(xiě)了Base類vtable的第一項(xiàng)。只要子類重寫(xiě)了基類的虛函數(shù),那么子類vtable相應(yīng)的項(xiàng)就會(huì)更改父類的vtable表項(xiàng)。 這一過(guò)程是編譯器自動(dòng)處理的,并且每個(gè)的類的vtable內(nèi)容都放在數(shù)據(jù)段里面。

3. 誰(shuí)讓對(duì)象與vtable綁到一起

上述代碼只是定義了每個(gè)類的vtable的內(nèi)容,但我們知道,帶有虛函數(shù)的對(duì)象在它內(nèi)部都有一個(gè)vtable指針,指向這個(gè)vtable,那么是何時(shí)指定的呢? 只要看看構(gòu)造函數(shù)的匯編代碼,就一目了然了:

Base::Base()函數(shù)的編譯代碼如下:

[cpp] view
plaincopyprint?

  1. _ZN4BaseC1Ev:  

  2. .LFB6:  

  3.     .cfi_startproc  

  4.     .cfi_personality 0x0,__gxx_personality_v0  

  5.     pushl   %ebp  

  6.     .cfi_def_cfa_offset 8  

  7.     movl    %esp, %ebp  

  8.     .cfi_offset 5, -8  

  9.     .cfi_def_cfa_register 5  

  10.     movl    8(%ebp), %eax  

  11.     movl    $_ZTV4Base+8, (%eax)  

  12.     popl    %ebp  

  13.     ret  

  14.     .cfi_endproc   

ZN4BaseC1Ev這個(gè)符號(hào)是C++函數(shù)Base::Base() 的內(nèi)部符號(hào)名,可使用c++flit將它還原。C++里的class,可以定義數(shù)據(jù)成員,函數(shù)成員兩種。但轉(zhuǎn)化到匯編層面時(shí),每個(gè)對(duì)象里面真正存放的是數(shù)據(jù)成員,以及虛函數(shù)表。

在上面的Base類中,由于沒(méi)有數(shù)據(jù)成員,因此它只有一個(gè)vtable指針。故Base類的定義,可以寫(xiě)成如下相應(yīng)的C代碼:

[cpp] view
plaincopyprint?

  1. struct Base {  

  2.     unsigned long **vtable;  

  3. }   

構(gòu)造函數(shù)中最關(guān)鍵的兩句是:

    movl    8(%ebp), %eax 
    movl    $_ZTV4Base+8, (%eax)


$_ZTV4Base+8 就是Base類的虛函數(shù)表的開(kāi)始位置,因此,構(gòu)造函數(shù)對(duì)應(yīng)的C代碼如下:

[cpp] view
plaincopyprint?

  1. void Base::Base(struct Base *this)  

  2. {  

  3.     this->vtable = &Base_vtable;  

  4. }  

同樣地,Derive類的構(gòu)造函數(shù)如下:

[cpp] view
plaincopyprint?

  1. struct Derive {  

  2.     unsigned long **vtable;  

  3. };  

  4. void Derive::Derive(struct Derive *this)  

  5. {  

  6.     this->vtable = &Derive_vtable;  

  7. }  

4. 實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)的最關(guān)鍵一步

在造構(gòu)函數(shù)里面設(shè)置好的vtable的值,顯然,同一類型所有對(duì)象內(nèi)的vtable值都是一樣的,并且永遠(yuǎn)不會(huì)改變。下面是main函數(shù)生成的匯編代碼,它展示了C++如何利用vtable來(lái)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)。

[cpp] view
plaincopyprint?

  1. .globl main  

  2.     .type   main, @function  

  3. main:  

  4. .LFB3:  

  5.     .cfi_startproc  

  6.     .cfi_personality 0x0,__gxx_personality_v0  

  7.     pushl   %ebp  

  8.     .cfi_def_cfa_offset 8  

  9.     movl    %esp, %ebp  

  10.     .cfi_offset 5, -8  

  11.     .cfi_def_cfa_register 5  

  12.     andl    $-16, %esp  

  13.     subl    $32, %esp  

  14.     leal    24(%esp), %eax  

  15.     movl    %eax, (%esp)  

  16.     call    _ZN6DeriveC1Ev  

  17.     leal    24(%esp), %eax  

  18.     movl    %eax, 28(%esp)  

  19.     movl    28(%esp), %eax  

  20.     movl    (%eax), %eax  

  21.     movl    (%eax), %edx  

  22.     movl    28(%esp), %eax  

  23.     movl    %eax, (%esp)  

  24.     call    *%edx  

  25.     movl    $0, %eax  

  26.     leave  

  27.     ret   

  28.     .cfi_endproc  

    andl    $-16, %esp 
    subl    $32, %esp

    這兩句是為局部變量d和bp在堆棧上分配空間,也即如下的語(yǔ)句:

Derive d;   
Base *pb;

leal    24(%esp), %eax 
movl    %eax, (%esp) 
call    _ZN6DeriveC1Ev

esp+24是變量d的首地址,先將它壓到堆棧上,然后調(diào)用d的構(gòu)造函數(shù),相應(yīng)翻譯成C語(yǔ)言則如下:

Derive::Dervice(&d);

leal    24(%esp), %eax 
movl    %eax, 28(%esp)

這里其實(shí)是將&d的值賦給pb,也即:

pb = &d;

最關(guān)鍵的代碼是下面這一段:

[cpp] view
plaincopyprint?

  1. movl    28(%esp), %eax  

  2. movl    (%eax), %eax  

  3. movl    (%eax), %edx  

  4. movl    28(%esp), %eax  

  5. movl    %eax, (%esp)  

  6. call    *%edx  

翻譯成C語(yǔ)言也就傳神的那句:

pb->vtable[0](bp);

編譯器會(huì)記住f虛函數(shù)放在vtable的第0項(xiàng),這是編譯時(shí)信息。

5. 小結(jié)

這里省略了很多關(guān)于編譯器和C++的細(xì)枝未節(jié),是出于討論方便用的需要。從上面的編譯代碼可以看到以下信息:

1.每個(gè)類都有各有的vtable結(jié)構(gòu),編譯會(huì)正確填寫(xiě)它們的虛函數(shù)表

2. 對(duì)象在構(gòu)造函數(shù)時(shí),設(shè)置vtable值為該類的虛函數(shù)表

3.在指針或者引用時(shí)調(diào)用虛函數(shù),是通過(guò)object->vtable加上虛函數(shù)的offset來(lái)實(shí)現(xiàn)的。

當(dāng)然這僅僅是g++的實(shí)現(xiàn)方式,它和VC++的略有不同,但原理是一樣的。

到此,相信大家對(duì)“c++中的虛函數(shù)”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


當(dāng)前文章:c++中的虛函數(shù)
文章出自:http://weahome.cn/article/pjjjjs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部