乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      C ++ 虛函數(shù)探索筆記(1)

       My鏡像站 2011-11-16

      鏈接:

      關(guā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修飾符

      啰嗦兩句

      虛函數(shù)在C++里的作用實(shí)在是非常非常的大,很多講述C++的文章都會(huì)講到它,要用好C++,就一定要學(xué)好虛函數(shù)。網(wǎng)絡(luò)上可以google到很多很 多關(guān)于它的文章,這一次的學(xué)習(xí),我不準(zhǔn)備去只是簡單的閱讀了解那些文章,而是希望通過編寫一些測試代碼,來對(duì)虛函數(shù)的一些實(shí)現(xiàn)機(jī)制,以及C++對(duì)象布局做 一下探索。

      虛函數(shù)的簡單示例 !

      虛函數(shù)常常出現(xiàn)在一些抽象接口類定義里,當(dāng)然,還有一個(gè)更常見的“特例”,那就是虛析構(gòu)函數(shù),后面會(huì)提到這個(gè)。

      下面是一段關(guān)于虛函數(shù)的簡單代碼,演示了使用基類接口操作對(duì)象時(shí)的效果:

      //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是一個(gè)只具有純虛函數(shù)的接口類,這個(gè)類不能被實(shí)例化,它唯一的用途就是抽象一些特定的接口函數(shù),當(dāng)然,在這里這個(gè)接口函數(shù)就是純虛函數(shù) parent1::fun1()。

      而類child1和child2則是兩個(gè)從parent1繼承的類,我們要使用它定義具體的類實(shí)例,所以它實(shí)現(xiàn)了由parent1繼承得來的fun1接口,并且各自的實(shí)現(xiàn)是不同的。

      函數(shù) test_func1 的參數(shù)是一個(gè)parent1類型的指針,它所要完成的功能就是調(diào)用這個(gè)parent1對(duì)象的fun1()函數(shù)。

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

      child1::fun1()

      child2::fun1()

      很顯然,在兩次調(diào)用test_func1函數(shù)的時(shí)候,雖然傳入的參數(shù)都是一個(gè)parent1的指針,但是卻都分別執(zhí)行了child1和child2 各自的fun1函數(shù)!這就是C++里類的多態(tài)。然而,這一切是怎么發(fā)生的呢?test_func1函數(shù)怎么會(huì)知道應(yīng)該調(diào)用哪個(gè)函數(shù)的呢?我不準(zhǔn)備像其他人 一樣畫若干圖來說明,我準(zhǔn)備用具體某個(gè)編譯器產(chǎn)生的對(duì)象布局以及相應(yīng)的匯編代碼來說明這個(gè)過程(這個(gè)編譯器是vs2008里的vc9)。

      我們先打開一個(gè)VS2008命令提示窗口,改變目錄到上面的代碼Win32Con.cpp所在目錄,輸入下面的命令:

      cl win32con.cpp /d1reportSingleClassLayoutchild

      上面的命令可以編譯win32con.cpp源碼,同時(shí)生成里面類名包含child 的類的對(duì)象布局(layout)

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

      輸入上面的命令后看到的對(duì)象布局如下,紅色字為我添加的注釋

      class child1    size(4): 子類child1的對(duì)象布局,只包含一個(gè)vfptr,大小為4字節(jié)
              +---
              | +--- (base class parent1) 這是被嵌套的父類parent1的對(duì)象布局
       0      | | {vfptr}
              | +---
              +---
      這是child1的vfptr所指的虛函數(shù)表的布局,只包含一個(gè)函數(shù)的地址,就是child1的fun1函數(shù)
      child1::$vftable@:
              | &child1_meta
              |  0
       0      | &child1::fun1
      
      child1::fun1 this adjustor: 0
      
      class child2    size(4): 子類child2的對(duì)象布局,只包含一個(gè)vfptr,大小為4字節(jié)
              +---
              | +--- (base class parent1) 這是被嵌套的父類parent1的對(duì)象布局
       0      | | {vfptr}
              | +---
              +---
      這是child2的vfptr所指的虛函數(shù)表的布局,只包含一個(gè)函數(shù)的地址,就是child2的fun1函數(shù)
      child2::$vftable@:
              | &child2_meta
              |  0
       0      | &child2::fun1
      
      child2::fun1 this adjustor: 0

      從上面的對(duì)象布局可以知道:

      • 每個(gè)子對(duì)象都有一個(gè)隱藏的成員變量vfptr(你當(dāng)然不能用這個(gè)名字訪問到它),它的值是指向該子對(duì)象的虛函數(shù)表,而虛函數(shù)表里填寫的函數(shù)地址是該子對(duì)象的fun1函數(shù)地址。
      • 對(duì)一個(gè)包含有虛函數(shù)的類做sizeof操作的時(shí)候,除了能直接看到的成員變量,還得增加4字節(jié)(在32位機(jī)器上),就是vfptr這個(gè)指針的大小。

      所以當(dāng)test_func1進(jìn)行pp->fun1()調(diào)用的時(shí)候,會(huì)首先取出pp所指的內(nèi)存地址并按照parent1的內(nèi)存布局,獲取到 vfptr指針(由于pp在兩次調(diào)用中分別指向co1和co2所以這里取得的實(shí)際上是co1的vfptr和co2的vfptr),然后從vfptr所指的 虛函數(shù)表第一項(xiàng)(現(xiàn)在也只有 1 項(xiàng))取出作為將要調(diào)用的函數(shù),由于co1和co2在各自的虛函數(shù)表里填寫了各自的fun1的地址,于是pp->fun1()最終就調(diào)用到了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,即對(duì)象的地址
      //取得對(duì)象的vfptr地址放到edx(因?yàn)関fptr在對(duì)象布局里拍在第一)
      001C1551  mov         edx,dword ptr [eax]
      001C1553  mov         esi,esp
      001C1555  mov         ecx,dword ptr [pp]
      001C1558  mov         eax,dword ptr [edx] //取出vfptr的第一個(gè)虛函數(shù)的地址到eax
      001C155A  call        eax //調(diào)用虛函數(shù),即fun1()

      至此,應(yīng)該比較清楚虛函數(shù)機(jī)制的基本實(shí)現(xiàn)了。然而,也許你還會(huì)有這些問題:

      • 虛函數(shù)表是每個(gè)子對(duì)象都有的么?
      • 虛函數(shù)是存在一個(gè)表里的,表的數(shù)據(jù)結(jié)構(gòu)是怎樣的,如何定位表里哪個(gè)才是我們要調(diào)用的虛函數(shù)?

      略作變化

      讓我們對(duì)前面的代碼做以下修改:

      • 定義一個(gè)普通類
      • 修改parent類,在fun1前增加虛函數(shù)fun2
      • 在child1里和child2里編寫fun2的具體實(shí)現(xiàn),一個(gè)在fun1之前編寫,另外一個(gè)在之后編寫

      修改后的編碼大致如下:

      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;
          }
      };

      然后我們?cè)偈褂胏l命令以及/d1reportSingleClassLayout選項(xiàng)輸出相關(guān)的類對(duì)象布局情況:

      class child     size(4):  //在普通類child里,看不到vfptr的身影!
              +---
       0      | a
              +---
      
      class child1    size(4):    //child1的對(duì)象布局,和之前沒有變化!
              +---
              | +--- (base class parent1)
       0      | | {vfptr}
              | +---
              +---
      //child1的虛函數(shù)表多了fun2,并且兩個(gè)虛函數(shù)在表里的順序相同于在parent類里聲明的順序
      child1::$vftable@:	     
              | &child1_meta
              |  0
       0      | &child1::fun2
       1      | &child1::fun1
      
      child1::fun1 this adjustor: 0
      child1::fun2 this adjustor: 0

      結(jié)論很明顯:

      • 虛函數(shù)表指針vfptr只在類里有虛擬函數(shù)的時(shí)候才會(huì)存在
      • 當(dāng)有多個(gè)虛函數(shù)的時(shí)候,虛函數(shù)在虛函數(shù)表里的順序由父類里虛函數(shù)的定義順序決定

      并且我們還可以觀察到:

      • 這個(gè)vfptr指針會(huì)放在類的起始處(這是必須的,vfptr在父類和子類的對(duì)象布局上必須一致!)
      • 虛函數(shù)表是以一個(gè)NULL指針標(biāo)識(shí)結(jié)束

      讓我們對(duì)這次簡單的示例代碼測試來做個(gè)小小總結(jié):

      • 有虛函數(shù)的類,一定會(huì)有一個(gè)虛函數(shù)表指針vfptr
      • 這個(gè)vfptr指針會(huì)放在類的起始處
      • 虛函數(shù)表里會(huì)按基類聲明虛函數(shù)的順序在vfptr里存放函數(shù)地址
      • 虛函數(shù)表里存放的是函數(shù)地址是具體子類的實(shí)現(xiàn)函數(shù)的地址
      • 調(diào)用虛函數(shù)的時(shí)候,是從vfptr所指的函數(shù)表里獲取到函數(shù)地址,然后才調(diào)用具體的代碼

      Tags: ,
      This entry was posted on Friday, July 31st, 2009 at 4:02 PM and is filed under C++. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

      4 Responses to “C++虛函數(shù)探索筆記(1)——虛函數(shù)的簡單示例分析”

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(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)論公約

        類似文章 更多