青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

chaosuper85

C++博客 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
  118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

  C++虛函數(shù)探索筆記(2)——虛函數(shù)與多繼承

    關(guān)注問(wèn)題:

    虛函數(shù)的作用

    虛函數(shù)的實(shí)現(xiàn)原理

    虛函數(shù)表在對(duì)象布局里的位置

    虛函數(shù)的類的sizeof

    純虛函數(shù)的作用

    多級(jí)繼承時(shí)的虛函數(shù)表內(nèi)容

    虛函數(shù)如何執(zhí)行父類代碼

    多繼承時(shí)的虛函數(shù)表定位,以及對(duì)象布局

    虛析構(gòu)函數(shù)的作用

    虛函數(shù)在QT的信號(hào)與槽中的應(yīng)用

    虛函數(shù)與inline修飾符,static修飾符

    前面我們嘗試了一個(gè)簡(jiǎn)單的例子,接下來(lái)嘗試一個(gè)多級(jí)繼承的例子,以及一個(gè)多繼承的例子。主要涉及到以下問(wèn)題:多級(jí)繼承時(shí)虛函數(shù)表的內(nèi)容是如何填寫的,如何在多級(jí)繼承的情況下調(diào)用某一級(jí)父類里的虛函數(shù),以及在多繼承(多個(gè)父類)的情況下的對(duì)象布局。

    多級(jí)繼承

    在這里,多級(jí)繼承指的是有3層或者多層繼承關(guān)系的情形。讓我們看看下面的代碼:
 //Source filename: Win32Con.cpp

 #include <iostream>
 using namespace std;
class parent1
 {
public:
    virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
    virtual int fun2()=0;
};
class child1:public parent1
 {
public:

    virtual int fun1()
    {
        cout<<"child1::fun1()"<<endl;
        parent1::fun1();
        return 0;
    }
    virtual int fun2()
    {
        cout<<"child1::fun2()"<<endl;
        return 0;
    }
};

class grandson:public child1
 {
public:
    virtual int fun2()
    {
        cout<<"grandson::fun2()"<<endl;
        //parent1::fun2();
        parent1::fun1();
        child1::fun2();
        return 0;
    }
};

void test_func1(parent1 *pp)
{
    pp->fun1();
    pp->fun2();
}

int main(int argc, char* argv[])
{
    grandson sunzi;
    test_func1(&sunzi);
    return 0;

 


    這段代碼展示了三個(gè)class,分別是parent1,child1,grandson.

    類parent1定義了兩個(gè)虛函數(shù),其中fun2是一個(gè)純虛函數(shù),這個(gè)類是一個(gè)不可實(shí)例化的抽象基類。

    類child1繼承了parent1,并且對(duì)兩個(gè)虛函數(shù)fun1和fun2都編寫了實(shí)現(xiàn)的代碼,這個(gè)類可以被實(shí)例化。

    類grandson繼承了child1,但是只對(duì)虛函數(shù)fun2編寫了實(shí)現(xiàn)的代碼。

    此外,我們還改寫了test_func1函數(shù),它的參數(shù)為parent1類型的指針,我們可以將parent1的子孫類作為這個(gè)函數(shù)的參數(shù)傳入。在這個(gè)函數(shù)里,我們將依次調(diào)用parent1類的兩個(gè)虛函數(shù)。

    可以先通過(guò)閱讀代碼預(yù)測(cè)一下程序的輸出內(nèi)容。

    程序的輸出內(nèi)容將是:

    child1::fun1()

    parent1::fun1()

    grandson::fun2()

    parent1::fun1()

    child1::fun2()

    先看第一行輸出child1::fun1(),為什么會(huì)輸出它呢?我們定義的具體對(duì)象sunzi是grandson類型的,test_func1的參數(shù)類型是parent1類型。在調(diào)用這個(gè)虛函數(shù)的時(shí)候,是完成了一次怎樣的調(diào)用過(guò)程呢?

    讓我們?cè)俅问褂胏l命令輸出這幾個(gè)類的對(duì)象布局:
 class parent1   size(4):
        +---
 0      | {vfptr}
        +---

parent1::$vftable@:
        | &parent1_meta
        |  0
 0      | &parent1::fun1
 1      | &parent1::fun2

parent1::fun1 this adjustor: 0
parent1::fun2 this adjustor: 0

class child1    size(4):
        +---
        | +--- (base class parent1)
 0      | | {vfptr}
        | +---
        +---

child1::$vftable@:
        | &child1_meta
        |  0
 0      | &child1::fun1
 1      | &child1::fun2

child1::fun1 this adjustor: 0
child1::fun2 this adjustor: 0

class grandson  size(4): //grandson的對(duì)象布局
        +---
        | +--- (base class child1)
        | | +--- (base class parent1)
 0      | | | {vfptr}
        | | +---
        | +---
        +---

grandson::$vftable@:  //grandson虛函數(shù)表的內(nèi)容
        | &grandson_meta
        |  0
 0      | &child1::fun1
 1      | &grandson::fun2

grandson::fun2 this adjustor: 0

 


    因?yàn)槲覀儗?shí)例化的是一個(gè)grandson對(duì)象,讓我們看看它的對(duì)象布局。正如前面的例子一樣,里面只有一個(gè)vfptr指針,但是不一樣的卻是這個(gè)指針?biāo)傅奶摵瘮?shù)表的內(nèi)容:

    第一個(gè)虛函數(shù),填寫的是child1類的fun1的地址;第二個(gè)虛函數(shù)填寫的才是grandson類的fun2的地址。

    很顯然我們可以得出這樣一個(gè)結(jié)論:在一個(gè)子對(duì)象的虛函數(shù)表里,每一個(gè)虛函數(shù)的實(shí)際運(yùn)行的函數(shù)地址,將填寫為在繼承體系里最后實(shí)現(xiàn)該虛函數(shù)的函數(shù)地址。

    所以當(dāng)我們?cè)趖est_func1里調(diào)用了傳入的parent1指針的fun1函數(shù)的時(shí)候,我們實(shí)際執(zhí)行的是填寫在虛函數(shù)表里的child1::fun1(),而調(diào)用fun2函數(shù)的時(shí)候,是從虛函數(shù)表里得到了grandson::fun2函數(shù)的地址并調(diào)用之。在“程序輸出結(jié)果”表里的第一行和第三行結(jié)果證實(shí)了上述結(jié)論。

    再看一下程序代碼部分的child1::fun1()的實(shí)現(xiàn)代碼,在第18行,我們有parent1::fun1();這樣的語(yǔ)句,這行代碼輸出了運(yùn)行結(jié)果里的第二行,而在grandson::fun2()的實(shí)現(xiàn)代碼第35行的parent1::fun1();以及第36行的child1::fun2();則輸出了運(yùn)行結(jié)果里的第四行和第五行的內(nèi)容。這三行代碼展示了如何調(diào)用父類以及更高的祖先類里的虛函數(shù)。——事實(shí)上,這與調(diào)用父類的普通函數(shù)沒有任何區(qū)別。

    在程序代碼的第34行,有一行被注釋了的內(nèi)容//parent1::fun2();,之所以會(huì)注釋掉,是因?yàn)檫@樣的代碼是無(wú)法通過(guò)編譯的,因?yàn)樵趐arent1類里,fun2是一個(gè)“純虛函數(shù)”也就是說(shuō)這個(gè)函數(shù)沒有代碼實(shí)體,在編譯的時(shí)候,鏈接器將無(wú)法找到fun2的目標(biāo)代碼從而報(bào)錯(cuò)。

    其實(shí)有了對(duì)虛函數(shù)的正確的認(rèn)識(shí),上面的多級(jí)繼承是很自然就能明白的。然而在多繼承的情況下,情況就有所不同了……

    多繼承下虛函數(shù)的使用

    假如一個(gè)類,它由多個(gè)父類繼承而來(lái),而在不同的父類的繼承體系里,都存在虛函數(shù)的時(shí)候,這個(gè)類的對(duì)象布局又會(huì)是怎樣的?它又是怎樣定位虛函數(shù)的呢?

    讓我們看看下面的代碼:
 //Source filename: Win32Con.cpp
 #include <iostream>
 using namespace std;
class parent1
 {
public:
    virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
};
class parent2
 {
public:
    virtual int fun2(){cout<<"parent2::fun2()"<<endl;return 0;};
};
class child1:public parent1,public parent2
 {
public:
    virtual int fun1()
    {
        cout<<"child1::fun1()"<<endl;
        return 0;
    }
    virtual int fun2()
    {
        cout<<"child1::fun2()"<<endl;
        return 0;
    }
};
void test_func1(parent1 *pp)
{
    pp->fun1();
}
void test_func2(parent2 *pp)
{
    pp->fun2();
}
int main(int argc, char* argv[])
{
    child1 chobj;
    test_func1(&chobj);
    test_func2(&chobj);
    return 0;
}
 


    這一次,我們有兩個(gè)父類,parent1和parent2,在parent1里定義了虛函數(shù)fun1,而在parent2里定義了虛函數(shù)fun2,然后我們有一個(gè)子類child1,在里面重新實(shí)現(xiàn)了fun1和 fun2兩個(gè)虛函數(shù)。然后我們編寫了test_func1函數(shù)來(lái)調(diào)用parent1類型對(duì)象的fun1函數(shù),編寫了test_func2函數(shù)調(diào)用parent2對(duì)象的fun2函數(shù)。在main函數(shù)里我們實(shí)例化了一個(gè)child1類型的對(duì)象chobj,然后分別傳給test_func1和test_func2去執(zhí)行。

    這段代碼的運(yùn)行結(jié)果非常簡(jiǎn)單就能看出來(lái):

    child1::fun1()

    child1::fun2()

    但是,讓我們看看對(duì)象布局吧:
 class child1    size(8):
        +---
        | +--- (base class parent1)
 0      | | {vfptr}
        | +---
        | +--- (base class parent2)
 4      | | {vfptr}
        | +---
        +---

child1::$vftable@parent1@:
        | &child1_meta
        |  0
 0      | &child1::fun1

child1::$vftable@parent2@:
        | -4
 0      | &child1::fun2

child1::fun1 this adjustor: 0
child1::fun2 this adjustor: 4

 


    注意到?jīng)]?在child1的對(duì)象布局里,出現(xiàn)了兩個(gè)vfptr指針!

    這兩個(gè)虛函數(shù)表指針?lè)謩e繼承于parent1和parent2類,分別指向了不同的兩個(gè)虛函數(shù)表。

    問(wèn)題來(lái)了,當(dāng)我們使用test_func1調(diào)用parent1類的fun1函數(shù)的時(shí)候,調(diào)用個(gè)過(guò)程還比較好理解,可以從傳入的地址參數(shù)取得繼承自parent1的vfptr,從而執(zhí)行正確的fun1函數(shù)代碼,但是當(dāng)我們調(diào)用test_func2函數(shù)的時(shí)候,為什么程序可以自動(dòng)取得來(lái)自parent2的vfptr呢,從而得出正確的fun2函數(shù)的地址呢?

    其實(shí),這個(gè)工作是編譯器自動(dòng)根據(jù)實(shí)例的類型完成的,在編譯階段就已經(jīng)確定了在調(diào)用test_func2的時(shí)候,傳入的this指針需要增加一定的偏移(在這里則是第一個(gè)vfptr所占用的大小,也就是4字節(jié))。

    我們可以看看main函數(shù)里這部分代碼的反匯編代碼:
   child1 chobj;
00F5162E 8D 4D F4         lea         ecx,[chobj]
00F51631 E8 F5 FB FF FF   call        child1::child1 (0F5122Bh)
    test_func1(&chobj);
00F51636 8D 45 F4         lea         eax,[chobj]
00F51639 50               push        eax
00F5163A E8 6F FB FF FF   call        test_func1 (0F511AEh)
00F5163F 83 C4 04         add         esp,4
    test_func2(&chobj);
00F51642 8D 45 F4         lea         eax,[chobj]
00F51645 85 C0            test        eax,eax
00F51647 74 0E            je          main+47h (0F51657h)
00F51649 8D 4D F4         lea         ecx,[chobj]
00F5164C 83 C1 04         add         ecx,4
00F5164F 89 8D 2C FF FF FF mov         dword ptr [ebp-0D4h],ecx
00F51655 EB 0A            jmp         main+51h (0F51661h)
00F51657 C7 85 2C FF FF FF 00 00 00 00 mov         dword ptr [ebp-0D4h],0
00F51661 8B 95 2C FF FF FF mov         edx,dword ptr [ebp-0D4h]
00F51667 52               push        edx
00F51668 E8 F6 FA FF FF   call        test_func2 (0F51163h)
00F5166D 83 C4 04         add         esp,4
    return 0;


    從第4行至第5行,執(zhí)行的是test_func1函數(shù),this指針指向 chobj (第2行l(wèi)ea ecx,[chobj]),但是調(diào)用test_func2函數(shù)的時(shí)候,this指針被增加了4(第14行)!于是,在test_func2執(zhí)行的時(shí)候,就可以從&chobj+4的地方獲得vfptr指針,從而根據(jù)parent2的對(duì)象布局得到了fun2的地址并執(zhí)行了。

    為了證實(shí)這點(diǎn),我們可以將代碼做如下的修改:
     1:  int main(int argc, char* argv[])

    2:  {

    3:      child1 chobj;

    4:      test_func1(&chobj);

    5:      test_func2((parent2 *)(void *)&chobj);

    6:      return 0;

    7:  }

    8:
 


    請(qǐng)注意紅色部分的變化,在講chobj傳入給test_func2之前,先用(void *)強(qiáng)制轉(zhuǎn)換為無(wú)類型指針,再轉(zhuǎn)換為parent2 指針,這樣的轉(zhuǎn)換,顯然是可行的,因?yàn)閏hobj本身就是parent2的子類,然而,程序的執(zhí)行效果卻是:

    child1::fun1()

    child1::fun1()

    執(zhí)行test_func2函數(shù),調(diào)用的是parent2::fun2,但是居然執(zhí)行的是child1::fun1()函數(shù)!!!

    這中間發(fā)生了些什么呢?我們?cè)倏纯捶磪R編的代碼:
   child1 chobj;
013D162E 8D 4D F4         lea         ecx,[chobj]
013D1631 E8 F5 FB FF FF   call        child1::child1 (13D122Bh)
    test_func1(&chobj);
013D1636 8D 45 F4         lea         eax,[chobj]
013D1639 50               push        eax
013D163A E8 6F FB FF FF   call        test_func1 (13D11AEh)
013D163F 83 C4 04         add         esp,4
    test_func2((parent2*)(void *)&chobj);
013D1642 8D 45 F4         lea         eax,[chobj]
013D1645 50               push        eax
013D1646 E8 18 FB FF FF   call        test_func2 (13D1163h)
013D164B 83 C4 04         add         esp,4
    return 0;
 


    從調(diào)用test_func2的反匯編代碼可以看到,這一次ecx寄存器的值沒有做改變!所以在執(zhí)行test_func2的時(shí)候,將取得parent1對(duì)象布局里的vfptr,而這個(gè)vfptr所指的虛函數(shù)表里的第一項(xiàng)就是fun1,并且被填寫為child1::fun1的地址了。所以才出現(xiàn)了child::fun1的輸出內(nèi)容!顯然這里有一個(gè)隱藏的致命問(wèn)題,加入parent1和parent2的第一個(gè)虛函數(shù)的參數(shù)列表不一致,這樣的調(diào)用顯然就會(huì)導(dǎo)致堆棧被破壞掉,程序99%會(huì)立即崩潰。之前的程序沒有崩潰并且成功輸出內(nèi)容,不過(guò)是因?yàn)閜arent1::fun1()和parent2::fun2()的參數(shù)列表一致的關(guān)系而已。

    所以,千萬(wàn)不要在使用一個(gè)多繼承對(duì)象的時(shí)候,將其類型信息丟棄,編譯器還需要依靠正確的類型信息,在使用虛函數(shù)的時(shí)候來(lái)得到正確的匯編代碼!

 

 

posted on 2009-08-05 17:45 chaosuper 閱讀(186) 評(píng)論(0)  編輯 收藏 引用

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲福利在线视频| 玖玖玖国产精品| 亚洲精选久久| 欧美一区在线视频| 亚洲在线一区| 欧美精品激情在线观看| 美国成人毛片| 国产亚洲一级高清| 亚洲永久在线| 午夜宅男久久久| 欧美日韩中文字幕| 亚洲精品在线免费| 亚洲美女一区| 欧美激情在线免费观看| 亚洲第一区色| 亚洲精品1234| 欧美黄色影院| 亚洲欧洲在线一区| 亚洲美女视频在线观看| 欧美96在线丨欧| 亚洲福利视频二区| 亚洲精品久久久久中文字幕欢迎你| 久久久久久自在自线| 久久久噜噜噜久久中文字免| 国产精品人人爽人人做我的可爱| 99精品免费视频| 亚洲视频图片小说| 国产精品久久久对白| 一区二区三区四区五区精品| 亚洲天堂网在线观看| 国产精品成人久久久久| 亚洲深夜福利网站| 久久精品国产亚洲精品 | 久久久久久综合网天天| 久久久精品国产99久久精品芒果| 国产欧美日韩麻豆91| 久久激情网站| 男男成人高潮片免费网站| 亚洲国产精品久久久久秋霞蜜臀| 欧美+亚洲+精品+三区| 亚洲国产女人aaa毛片在线| 亚洲精品免费一区二区三区| 欧美日韩亚洲一区二区三区在线观看 | 欧美一区二区三区免费大片| 国产一区二区毛片| 蜜桃av噜噜一区| 日韩亚洲综合在线| 欧美亚洲一区| 亚洲福利视频二区| 欧美日韩麻豆| 欧美一级在线亚洲天堂| 欧美成黄导航| 亚洲一区www| 精品成人一区二区| 欧美日韩综合视频网址| 性色av一区二区怡红| 亚洲国产高清aⅴ视频| 亚洲中字在线| 亚洲国产mv| 国产精品日韩在线播放| 久久久久久久久蜜桃| 亚洲精品国产精品久久清纯直播| 午夜亚洲一区| 亚洲激情综合| 国产偷久久久精品专区| 欧美国产极速在线| 欧美亚洲日本国产| 日韩写真视频在线观看| 久久综合九色综合欧美就去吻| 亚洲美女尤物影院| 国外成人在线| 国产精品久久久久久久久久久久久久| 久久久久久欧美| 亚洲一区二区视频在线| 亚洲丰满在线| 老巨人导航500精品| 亚洲欧美国内爽妇网| 亚洲国产精品传媒在线观看 | 国产午夜精品麻豆| 欧美日韩激情网| 另类激情亚洲| 久久国产精品99国产| 亚洲视频免费看| 亚洲精品国产视频| 蜜桃久久精品乱码一区二区| 先锋影音网一区二区| 99这里只有精品| 亚洲欧洲日本一区二区三区| 国产亚洲精品一区二区| 欧美日韩精品二区第二页| 噜噜噜91成人网| 欧美专区日韩视频| 西西人体一区二区| 在线亚洲精品| 一个人看的www久久| 亚洲精品免费电影| 欧美激情欧美激情在线五月| 久久色在线播放| 久久精品一区二区三区不卡| 欧美一区二区三区播放老司机| 在线亚洲欧美专区二区| 99亚洲一区二区| 一区二区三区免费网站| 99国产精品视频免费观看| 亚洲欧洲在线看| 亚洲美女一区| 亚洲视频电影在线| 亚洲永久在线| 午夜欧美不卡精品aaaaa| 性欧美精品高清| 欧美一区二区私人影院日本 | 久久综合伊人77777尤物| 久久精品首页| 久久一区二区三区超碰国产精品| 久久国产欧美| 男人天堂欧美日韩| 亚洲国产精品国自产拍av秋霞| 欧美搞黄网站| 99国产麻豆精品| 亚洲男人第一av网站| 欧美一级片在线播放| 久久久夜夜夜| 欧美电影打屁股sp| 欧美日韩国产影院| 国产片一区二区| 永久91嫩草亚洲精品人人| 亚洲激情在线视频| 亚洲一区精彩视频| 久久久久一区二区三区| 亚洲高清色综合| 一区二区av| 久久经典综合| 欧美精品v日韩精品v韩国精品v | 久久婷婷久久一区二区三区| 你懂的视频欧美| 夜夜夜久久久| 欧美中文字幕精品| 欧美激情精品久久久六区热门| 欧美日韩亚洲一区三区| 国产一区二区三区高清播放| 亚洲精品久久久久久久久久久久久| 中国av一区| 美女主播精品视频一二三四| 亚洲人成在线观看| 欧美在线短视频| 欧美日韩精品福利| 激情久久婷婷| 亚洲欧美另类国产| 亚洲第一页中文字幕| 亚洲一区二区三区四区五区午夜 | 欧美主播一区二区三区美女 久久精品人| 久久看片网站| 国产九九精品视频| 99视频超级精品| 狂野欧美激情性xxxx欧美| 一本色道久久综合精品竹菊| 久久综合久久久| 国产精品一区二区a| 亚洲精品影院在线观看| 久久久久久亚洲精品杨幂换脸| 91久久国产综合久久| 久久久久久久一区二区| 国产精品久久久久免费a∨大胸| 极品少妇一区二区| 午夜精品久久久久久久久久久| 欧美成人亚洲成人| 欧美一区午夜视频在线观看| 国产精品扒开腿爽爽爽视频| 亚洲三级免费| 免费在线成人av| 欧美影院一区| 国产精品视频网| 亚洲天堂黄色| 日韩视频永久免费观看| 男女视频一区二区| 在线成人免费视频| 久久青青草综合| 欧美一区二区三区久久精品| 国产精品福利在线| 亚洲少妇在线| 日韩视频―中文字幕| 欧美日本不卡视频| 日韩亚洲不卡在线| 亚洲激情不卡| 欧美国产视频一区二区| 亚洲国产二区| 亚洲成人自拍视频| 欧美国产精品| aa级大片欧美三级| 艳女tv在线观看国产一区| 欧美精品一区在线播放| 亚洲美女免费视频| 日韩视频中文字幕| 欧美四级电影网站| 亚洲在线一区| 午夜精品成人在线| 国内自拍一区| 欧美国产日本在线| 欧美日韩成人免费| 亚洲欧美日韩一区在线|