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

chaosuper85

C++博客 首頁 新隨筆 聯系 聚合 管理
  118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

    C++虛函數探索筆記(1)——虛函數的簡單示例分析

    關注問題:

    虛函數的作用

    虛函數的實現原理

    虛函數表在對象布局里的位置

    虛函數的類的sizeof

    純虛函數的作用

    多級繼承時的虛函數表內容

    虛函數如何執行父類代碼

    多繼承時的虛函數表定位,以及對象布局

    虛析構函數的作用

    虛函數在QT的信號與槽中的應用

    虛函數與inline修飾符,static修飾符

    啰嗦兩句

    虛函數在C++里的作用是在是非常非常的大,很多講述C++的文章都會講到它

,要用好C++,就一定要學好虛函數。網絡上可以google到很多很多關于它的文章

,這一次的學習,我不準備去只是簡單的閱讀了解那些文章,而是希望通過編寫

一些測試代碼,來對虛函數的一些實現機制,以及C++對象布局做一下探索。

    虛函數的簡單示例 !

    虛函數常常出現在一些抽象接口類定義里,當然,還有一個更常見的“特例

”,那就是虛析構函數,后面會提到這個。

    下面是一段關于虛函數的簡單代碼,演示了使用基類接口操作對象時的效果


 //Source filename: Win32Con.cpp
#include <iostream>
using namespace std;
class parent1
{
public:
    virtual int fun1()=0;
};

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

class child2:public parent1
{
public:
    virtual int fun1()
    {
        cout<<"child2::fun1()"<<endl;
        return 0;
    }
};

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

int main(int argc, char* argv[])
{
    child1 co1;
    child2 co2;
    test_func1(&co1);
    test_func1(&co2);
    return 0;
}

 


    在上面的代碼里,類parent1是一個只具有純虛函數的接口類,這個類不能被

實例化,它唯一的用途就是抽象一些特定的接口函數,當然,在這里這個接口函

數就是純虛函數 parent1::fun1()。

    而類child1和child2則是兩個從parent1繼承的類,我們要使用它定義具體的

類實例,所以它實現了由parent1繼承得來的fun1接口,并且各自的實現是不同的

    函數 test_func1 的參數是一個parent1類型的指針,它所要完成的功能就是

調用這個parent1對象的fun1()函數。

    讓我們編譯運行一下上面的代碼,可以看到下面的輸出

    child1::fun1()

    child2::fun1()

    很顯然,在兩次調用test_func1函數的時候,雖然傳入的參數都是一個

parent1的指針,但是卻都分別執行了child1和child2各自的fun1函數!這就是

C++里類的多態。然而,這一切是怎么發生的呢?test_func1函數怎么會知道應該

調用哪個函數的呢?我不準備像其他人一樣畫若干圖來說明,我準備用具體某個

編譯器產生的對象布局以及相應的匯編代碼來說明這個過程(這個編譯器是

vs2008里的vc9)。

    我們先打開一個VS2008命令提示窗口,改變目錄到上面的代碼Win32Con.cpp

所在目錄,輸入下面的命令:

    cl  win32con.cpp  /d1reportSingleClassLayoutchild

    上面的命令可以編譯win32con.cpp源碼,同時生成里面類名包含child 的類

的對象布局(layout)

    注意:d1reportSingleClassLayout和后面的child是相連的!

    輸入上面的命令后看到的對象布局如下,紅色字為我添加的注釋
 class child1    size(4): 子類child1的對象布局,只包含一個vfptr,大小為

4字節
        +---
        | +--- (base class parent1) 這是被嵌套的父類parent1的對象布局
 0      | | {vfptr}
        | +---
        +---
這是child1的vfptr所指的虛函數表的布局,只包含一個函數的地址,就是child1

的fun1函數
child1::$vftable@:
        | &child1_meta
        |  0
 0      | &child1::fun1

child1::fun1 this adjustor: 0

class child2    size(4): 子類child2的對象布局,只包含一個vfptr,大小為4

字節
        +---
        | +--- (base class parent1) 這是被嵌套的父類parent1的對象布局
 0      | | {vfptr}
        | +---
        +---
這是child2的vfptr所指的虛函數表的布局,只包含一個函數的地址,就是child2

的fun1函數
child2::$vftable@:
        | &child2_meta
        |  0
 0      | &child2::fun1

child2::fun1 this adjustor: 0

 


    從上面的對象布局可以知道:

    每個子對象都有一個隱藏的成員變量vfptr(你當然不能用這個名字訪問到它

),它的值是指向該子對象的虛函數表,而虛函數表里填寫的函數地址是該子對

象的fun1函數地址。

    對一個包含有虛函數的類做sizeof操作的時候,除了能直接看到的成員變量,還得增加4字節(在32位機器上),就是vfptr這個指針的大小。

    所以當test_func1進行pp->fun1()調用的時候,會首先取出pp所指的內存地址并按照parent1的內存布局,獲取到vfptr指針(由于pp在兩次調用中分別指向co1和co2所以這里取得的實際上是co1的vfptr和co2的vfptr),然后從vfptr所指的虛函數表第一項(現在也只有 1 項)取出作為將要調用的函數,由于co1和co2在各自的虛函數表里填寫了各自的fun1的地址,于是pp->fun1()最終就調用到了co1和co2各自的fun1,輸出自然也就不同了。

    讓我們看看test_func1的反匯編代碼:
 void test_func1(parent1 *pp)
{
001C1530  push        ebp
001C1531  mov         ebp,esp
001C1533  sub         esp,0C0h
001C1539  push        ebx
001C153A  push        esi
001C153B  push        edi
001C153C  lea         edi,[ebp-0C0h]
001C1542  mov         ecx,30h
001C1547  mov         eax,0CCCCCCCCh
001C154C  rep stos    dword ptr es:[edi]
    pp->fun1();
001C154E  mov         eax,dword ptr [pp] //取得pp的值放到eax,即對象的地址
//取得對象的vfptr地址放到edx(因為vfptr在對象布局里拍在第一)
001C1551  mov         edx,dword ptr [eax]
001C1553  mov         esi,esp
001C1555  mov         ecx,dword ptr [pp]
001C1558  mov         eax,dword ptr [edx] //取出vfptr的第一個虛函數的地址到eax
001C155A  call        eax //調用虛函數,即fun1()

    至此,應該比較清楚虛函數機制的基本實現了。然而,也許你還會有這些問題:

    虛函數表是每個子對象都有的么?

    虛函數是存在一個表里的,表的數據結構是怎樣的,如何定位表里哪個才是我們要調用的虛函數?

    略作變化

    讓我們對前面的代碼做以下修改:

    定義一個普通類

    修改parent類,在fun1前增加虛函數fun2

    在child1里和child2里編寫fun2的具體實現,一個在fun1之前編寫,另外一個在之后編寫修改后的編碼大致如下:

 class parent1
{
public:
    virtual int fun2()=0;
    virtual int fun1()=0;
};

class child
{
    int a;
};

class child1:public parent1
{
public:

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

    然后我們再使用cl命令以及/d1reportSingleClassLayout選項輸出相關的類對象布局情況:

 class child     size(4):  //在普通類child里,看不到vfptr的身影!
        +---
 0      | a
        +---

class child1    size(4):    //child1的對象布局,和之前沒有變化!
        +---
        | +--- (base class parent1)
 0      | | {vfptr}
        | +---
        +---
//child1的虛函數表多了fun2,并且兩個虛函數在表里的順序相同于在parent類里聲明的順序
child1::$vftable@:
        | &child1_meta
        |  0
 0      | &child1::fun2
 1      | &child1::fun1

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

    結論很明顯:

    虛函數表指針vfptr只在類里有虛擬函數的時候才會存在當有多個虛函數的時候,虛函數在虛函數表里的順序由父類里虛函數的定義順序決定并且我們還可以觀察到:

    這個vfptr指針會放在類的起始處(這是必須的,vfptr在父類和子類的對象布局上必須一致!)

    虛函數表是以一個NULL指針標識結束讓我們對這次簡單的示例代碼測試來做個小小總結:

    有虛函數的類,一定會有一個虛函數表指針vfptr這個vfptr指針會放在類的起始處虛函數表里會按基類聲明虛函數的順序在vfptr里存放函數地址虛函數表里存放的是函數地址是具體子類的實現函數的地址調用虛函數的時候,是從vfptr所指的函數表里獲取到函數地址,然后才調用具體的代碼。

 


posted on 2009-08-05 17:42 chaosuper 閱讀(354) 評論(1)  編輯 收藏 引用

Feedback

# re: C++中虛函數深度探索 一 (轉載)[未登錄] 2009-08-06 23:15 ttylikl
呵呵,老兄,留個原文鏈接嘛!  回復  更多評論
  


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   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>
            蜜臀av性久久久久蜜臀aⅴ四虎| 欧美在线观看一区| 欧美ed2k| 日韩视频在线一区二区三区| 亚洲国产91色在线| 暖暖成人免费视频| av成人天堂| 亚洲免费一级电影| 国内精品99| 亚洲电影在线看| 欧美三日本三级少妇三99| 亚洲欧美日本另类| 性欧美大战久久久久久久免费观看 | 国产一区二区三区四区| 久久综合一区二区三区| 女同性一区二区三区人了人一| 最新亚洲电影| 一区二区三区免费网站| 国产亚洲美州欧州综合国| 牛牛影视久久网| 欧美三级在线| 免费在线观看精品| 欧美日韩在线精品| 久久天天躁狠狠躁夜夜爽蜜月| 免费久久99精品国产| 亚洲夜间福利| 久久综合网络一区二区| 亚洲一区二区不卡免费| 久久久亚洲国产美女国产盗摄| 一本色道久久综合狠狠躁篇的优点 | 久久久精品五月天| 欧美精品在线播放| 欧美在线999| 欧美激情一区二区三区不卡| 欧美有码在线视频| 欧美美女bb生活片| 久久久久久97三级| 欧美日本在线看| 久热综合在线亚洲精品| 国产精品国码视频| 亚洲国产精品va在看黑人| 国产视频亚洲精品| 在线视频你懂得一区| 亚洲电影在线播放| 西西人体一区二区| 亚洲在线视频观看| 欧美极品一区二区三区| 久热爱精品视频线路一| 国产毛片一区二区| 99精品热6080yy久久| 亚洲国内自拍| 久久久久一区| 久久久欧美精品| 欧美午夜无遮挡| 亚洲国产天堂久久综合| 在线观看91精品国产麻豆| 亚洲欧美日韩在线不卡| 国产精品99久久久久久久久久久久| 久久一区二区三区超碰国产精品| 欧美一级专区免费大片| 欧美日韩在线视频首页| 亚洲乱码国产乱码精品精可以看 | 欧美电影免费观看网站| 欧美 日韩 国产 一区| 国产欧美一区二区三区久久 | 久久激情视频久久| 久久尤物电影视频在线观看| 国产日本欧美一区二区| 亚洲欧美在线网| 欧美在线视频免费播放| 国产欧美日韩一区| 欧美在线观看网站| 久久综合给合| 亚洲激情网站| 欧美日韩视频一区二区三区| 日韩视频在线一区二区三区| 亚洲一级网站| 国产日韩欧美在线一区| 久久激五月天综合精品| 免费观看在线综合色| 亚洲国产综合视频在线观看| 欧美乱妇高清无乱码| 中文精品在线| 久久成人免费日本黄色| 一色屋精品亚洲香蕉网站| 美女精品在线观看| 99精品欧美一区二区三区| 西瓜成人精品人成网站| 狠狠88综合久久久久综合网| 麻豆91精品91久久久的内涵| 亚洲欧洲日夜超级视频| 香蕉免费一区二区三区在线观看| 国产区亚洲区欧美区| 久久性天堂网| 一区二区欧美日韩视频| 久久免费视频这里只有精品| 亚洲国产另类精品专区 | 久久蜜臀精品av| 亚洲黄色影院| 欧美一区二视频在线免费观看| 国产综合色一区二区三区 | 亚洲一区二区三区精品动漫| 欧美在线不卡视频| 亚洲国语精品自产拍在线观看| 欧美日韩性生活视频| 久久动漫亚洲| 亚洲毛片在线观看| 久久综合激情| 亚洲欧美日韩视频二区| 亚洲精品免费电影| 国产一区二区三区成人欧美日韩在线观看 | 国产欧美二区| 欧美黄色大片网站| 欧美一区二区三区精品电影| 亚洲国产精品久久久久婷婷老年| 亚洲欧美另类国产| 亚洲精选中文字幕| 国模精品一区二区三区色天香| 欧美国产91| 欧美在线啊v一区| 日韩一区二区免费看| 久久全球大尺度高清视频| 亚洲一区二区免费看| 樱花yy私人影院亚洲| 国产女人精品视频| 欧美日韩一区二区精品| 欧美高清在线视频观看不卡| 久久精品国产亚洲aⅴ| 亚洲一区二区伦理| 夜色激情一区二区| 亚洲电影免费观看高清完整版在线 | 欧美激情国产日韩| 久久香蕉精品| 久久久噜噜噜久久狠狠50岁| 欧美一区1区三区3区公司| 亚洲在线视频| 亚洲永久网站| 亚洲一区二区三区午夜| 亚洲视频www| 一本色道久久综合狠狠躁篇的优点| 一区二区三区在线免费播放| 国产性天天综合网| 国产日韩精品一区二区| 国产精品一区久久久| 国产精品夜夜夜| 国产精品久久久久免费a∨| 欧美亚州一区二区三区 | 欧美激情一区二区久久久| 蜜臀a∨国产成人精品| 免费成年人欧美视频| 蜜臀av一级做a爰片久久| 你懂的国产精品| 欧美成人精品1314www| 欧美福利一区二区| 欧美日本国产视频| 欧美三日本三级少妇三2023 | 久久成人免费视频| 久久久精品网| 欧美国产视频日韩| 欧美精品一区二区三区一线天视频| 午夜视频在线观看一区| 亚洲女优在线| 麻豆成人精品| 午夜视频精品| 亚洲欧美日韩区| 欧美人与性禽动交情品 | 亚洲精品乱码久久久久久蜜桃91| 欧美日韩国产综合网| 亚洲激情亚洲| 亚洲理论电影网| 欧美国产在线电影| 亚洲高清视频在线| aa国产精品| 国产伦精品一区二区三区四区免费 | 亚洲狼人综合| 午夜精品影院| 蜜臀久久99精品久久久画质超高清| 欧美猛交免费看| 国产综合在线看| 99re热精品| 久久黄色影院| 亚洲国产综合视频在线观看| 亚洲素人在线| 欧美 日韩 国产在线| 欧美性猛交99久久久久99按摩| 国产一区在线视频| 一区二区欧美在线| 噜噜噜久久亚洲精品国产品小说| 亚洲精品欧美| 午夜一级久久| 欧美日本三区| **网站欧美大片在线观看| 亚洲欧美日韩国产一区二区| 欧美大片国产精品| 欧美一区二区成人| 国产精品久久久| 日韩视频一区二区三区在线播放免费观看| 亚洲欧美在线一区二区| 亚洲国产精品成人| 久久精品一区中文字幕|