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

以題論道----關于虛函數的一些解讀

                                    peakflys原創作品,轉載請注明源作者和源鏈接!
    virtual function是很多公司面試題的重點考察內容,雖然對于C++而言這是一個老生常談的話題了,但是工作中我發現還是有很多人理解的不透徹。
    先看下面的一個例子:
/**
 *\brief virtual function test case
 *\author peakflys
 *\date Sun Dec  1 14:52:47 CST 2013
 */
#include <iostream>
using namespace std;
class Base
{
public:
    virtual void print(const int a = 10) {cout<<"Base: "<<a<<endl;}
};
class Derive : public Base
{
public:
    virtual void print(const int a = 100) {cout<<"Derive: "<<a<<endl;}
};
int main()
{
    Base *pb = new Derive;
    pb->print();
    Base& rb = *pb;
    rb.print();
    Derive d;
    d.print();
    Base *pbb = &d; 
    pbb->print();
    Base& rbb = d;
    rbb.print();
    Base b;
    b.print();
    Derive *pd = (Derive*)&b;
    pd->print();
    Derive& rd = *(Derive*)&b;
    rd.print();
    delete pb; 
    return 0;
}
你認為運行后的結果是什么呢?
下面是在我機器上的運行結果(Linux dev 2.6.32,gcc (GCC) 4.8.1)
Derive: 10
Derive: 10
Derive: 100
Derive: 10
Derive: 10
Base: 10
Base: 100
Base: 100
上面例子主要考察的內容有四塊:虛函數的執行、引用和指針的關系、函數調用過程、類型強轉后的行為。如果你能答對所有的結果,下面的內容可以略過。
下面我們來一一回顧一下所涉及到的這四塊內容。
1、虛函數的運行機理:
虛函數是C++實現多態性的必要手段,它在運行時刻才決定具體該調用哪個函數。對于虛函數的完整細節實現標準并未給出,但是大多數編譯器廠商,包括GCC、VS的常見實現都是在含有虛函數的類對象起始地址增加一個虛表指針,虛表指針指向的數組空間稱之為虛表,這個數組包含了類對象的所有虛函數地址。詳細內容大家可以參看《Inside The C++ Object Model》的Function語義學(注:這本書里有部分結論和例子運行同現在主流編譯器的實現有出入)。
2、引用的行為
在常見的編譯器中,引用一般都是通過指針來實現的,它同指針的區別就是它比指針有更多的約束,使用上有更多的限制。
3、虛函數的調用過程:
虛函數的調用過程通常是以下三個步驟:
①、參數壓棧
②、從虛表指針指向的虛表中找出函數的地址
③、調用函數。
這些操作都是在編譯時期就確定的,所不同的是運行時刻對象不同,其對應的虛表中函數地址自然也就是運行時真實對象的函數,這也就是虛函數實現的本質。
而這個過程中,參數的入棧是對象無關的,而且是在編譯時期就確定下來的。所以上面例子中所有指針和引用所調用函數的參數,都是指針和引用本身類型對應的函數默認參數,同運行時刻他們真實指向的對象內存無關。
4、類型強轉后的行為
通常的類型強轉是告訴編譯器必須按照指定結構的內存布局來解析對應內存,正如上例中”Derive *pd = (Derive*)&b; “ ,編譯器就會把b對應的內存來當做Derive的內存布局來解析,但是內存里的內容不變,所以虛函數運行正常。
注:這種行為很危險,如果使用的內存布局并不適合真實內存,很可能造成訪問越界等問題,所以要格外小心強轉操作的使用!對于例子中的downcasting行為,建議使用C++提供的dynamic_cast來轉換。
為了大家更好的理解上面的內容,特附上使用指針和引用分別調用虛函數過程的gcc匯編代碼和注釋:
    Base *pb = new Derive;
  400b49:   bf 08 00 00 00          mov    $0x8,%edi
  400b4e:   e8 6d fe ff ff          callq  4009c0 <_Znwm@plt>
  400b53:   48 89 c3                mov    %rax,%rbx
  400b56:   48 89 df                mov    %rbx,%rdi
  400b59:   e8 f4 01 00 00          callq  400d52 <_ZN6DeriveC1Ev>   //以上均為Derive對象的構造
   400b5e:   48 89 5d e8             mov    %rbx,-0x18(%rbp)             //pb指針的賦值
    pb->print();
  400b62:   48 8b 45 e8             mov    -0x18(%rbp),%rax               //pb指針指向的內存的首地址,即Derive對象的起始地址,亦即虛表指針的地址
  400b66:   48 8b 00                mov    (%rax),%rax                        //取虛表地址
  400b69:   48 8b 00                mov    (%rax),%rax                        //取虛表中的第一項內容(因Derive和Base只有一個虛函數),即print函數地址
  400b6c:   48 8b 55 e8             mov    -0x18(%rbp),%rdx               //this指針傳入rdx
  400b70:   be 0a 00 00 00          mov    $0xa,%esi                         //參數10入棧(可見在編譯時期就已經確定了)
  400b75:   48 89 d7                mov    %rdx,%rdi                            //this指針借rdx傳給rdi
  400b78:   ff d0                   callq  *%rax                                     //調用虛函數(通過真實對象的虛表來確定的真正被調函數)
    Base& rb = *pb;
  400b7a:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  400b7e:   48 89 45 e0             mov    %rax,-0x20(%rbp)
    rb.print();
  400b82:   48 8b 45 e0             mov    -0x20(%rbp),%rax
  400b86:   48 8b 00                mov    (%rax),%rax
  400b89:   48 8b 00                mov    (%rax),%rax
  400b8c:   48 8b 55 e0             mov    -0x20(%rbp),%rdx
  400b90:   be 0a 00 00 00          mov    $0xa,%esi
  400b95:   48 89 d7                mov    %rdx,%rdi
  400b98:   ff d0                   callq  *%rax                                       //以上為通過引用調用虛函數的過程,可見同指針調用的實現完全相同,注釋略
通過上面的分析,相信大家應該都能輕松的明白上面例子的運行結果,此處不再一一解讀。
                                                         --by peakflys 15:57:06 Sunday, December 01, 2013

posted on 2013-12-01 16:08 peakflys 閱讀(3022) 評論(7)  編輯 收藏 引用 所屬分類: C++

評論

# re: 以題論道----關于虛函數的一些解讀 2013-12-01 19:05 Richard Wei

effective C++ item 38  回復  更多評論   

# re: 以題論道----關于虛函數的一些解讀 2013-12-02 09:32 peakflys

僅作例子講解@Richard Wei
  回復  更多評論   

# re: 以題論道----關于虛函數的一些解讀 2013-12-03 11:21 zdd

最后一個類型轉換,這樣也可以。不知道兩者有何區別。
Derive& rd = (Derive&)b;
rd.print();  回復  更多評論   

# re: 以題論道----關于虛函數的一些解讀 2013-12-04 09:47 NWAO

".....上面例子中所有指針和引用所調用函數的參數,都是指針和引用本身類型對應的函數默認參數,同運行時刻他們真實指向的對象內存無關。"

這句話加紅一下就更好了, 很重要的,同學們可以參考Effective C++ ITEM 37.  回復  更多評論   

# re: 以題論道----關于虛函數的一些解讀 2013-12-04 23:45 vsgoster

感謝,很受用~特別是默認參數的入棧,從沒考慮過這個問題。  回復  更多評論   

# re: 以題論道----關于虛函數的一些解讀[未登錄] 2013-12-05 14:24 peakflys

@zdd 兩者沒有本質區別。
*(Derive*)&b 先取地址,強轉地址類型,然后再取內容,同匯編實現基本是一一對應起來的;
(Derive&)b是使用C式的強轉直接把內容轉成Derive的引用,編譯器幫你翻譯成的匯編代碼實現應該和上面的是一樣的。  回復  更多評論   

# re: 以題論道----關于虛函數的一些解讀 2013-12-12 00:00 Hacksign

博主的例子,有一個坑,具有默認參數的virtual函數執行靜態綁定。所以,如果去掉參數,即virtual void print(void),那么第一個print調用應該執行derive版本的print。  回復  更多評論   

<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

導航

統計

公告

人不淡定的時候,就愛表現出來,敲代碼如此,偶爾的靈感亦如此……

常用鏈接

留言簿(4)

隨筆分類

隨筆檔案

文章檔案

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产精品腿扒开做爽爽爽挤奶网站| 免费成人网www| 欧美午夜精品理论片a级按摩| 亚洲精选成人| 亚洲精品欧美日韩| 欧美三级视频在线| 亚洲免费综合| 香蕉国产精品偷在线观看不卡| 国产欧美日本在线| 久久久99久久精品女同性| 午夜精品视频在线观看| 激情综合久久| 亚洲国产老妈| 欧美人交a欧美精品| 亚洲夜间福利| 久久激五月天综合精品| 亚洲激情中文1区| 日韩午夜三级在线| 国产日产精品一区二区三区四区的观看方式 | 欧美一级欧美一级在线播放| 亚洲免费在线视频一区 二区| 国产一区二区三区成人欧美日韩在线观看 | 亚洲美女视频在线免费观看| 国产精品久久久久久久久久免费 | 欧美国产第一页| 欧美精品久久99| 欧美一区二区视频观看视频| 久久精品欧美日韩| 一本色道精品久久一区二区三区| 中文在线资源观看视频网站免费不卡| 国产视频精品xxxx| 91久久精品一区二区别| 国产欧美精品在线播放| 久久永久免费| 国产精品久久久99| 亚洲第一天堂无码专区| 国产精品亚洲一区二区三区在线| 欧美成人午夜| 国产欧美日韩三区| 91久久中文| 激情六月婷婷久久| 亚洲一区二区伦理| 99re6热只有精品免费观看 | 亚洲品质自拍| 伊人成人开心激情综合网| 9l国产精品久久久久麻豆| 禁久久精品乱码| 亚洲综合国产精品| 99精品热6080yy久久 | 亚洲精品一区二区三区av| 国产一区激情| 亚洲一区国产一区| 亚洲午夜精品| 欧美日韩情趣电影| 亚洲高清视频一区| 在线国产亚洲欧美| 欧美综合国产| 久久久精彩视频| 国产农村妇女毛片精品久久莱园子 | 午夜在线成人av| 欧美四级电影网站| 亚洲精品一区二区在线观看| 亚洲欧洲日韩综合二区| 久久一区二区精品| 美女主播一区| 在线看国产一区| 久久久久在线观看| 久久综合色播五月| 国内成人精品2018免费看 | 亚洲六月丁香色婷婷综合久久| 激情久久五月| 久久国产精品一区二区| 欧美一区二区在线播放| 国产精品久久久对白| 亚洲一区在线观看视频| 欧美一区二区三区四区在线观看地址| 国产精品日本欧美一区二区三区| 99精品久久久| 亚洲图色在线| 国产精品白丝黑袜喷水久久久| 亚洲美女在线观看| 亚洲综合日韩| 国产一区在线免费观看| 久久亚洲私人国产精品va| 亚洲第一精品久久忘忧草社区| 99国产精品视频免费观看一公开| 欧美精品一区二区三区视频| 日韩视频免费观看高清完整版| 在线综合视频| 国产日韩欧美在线看| 久久天堂精品| 99热精品在线| 久久久午夜电影| 亚洲伦理久久| 国产乱码精品一区二区三| 午夜精品久久久久久久99水蜜桃| 开心色5月久久精品| 日韩视频二区| 国产丝袜一区二区三区| 免费观看欧美在线视频的网站| 亚洲全黄一级网站| 欧美在线在线| 99re6这里只有精品| 国产日韩精品一区观看| 美女图片一区二区| 亚洲永久免费观看| 欧美大片免费观看| 欧美一级理论性理论a| 亚洲国内欧美| 国产日韩在线一区二区三区| 欧美 日韩 国产一区二区在线视频| 亚洲视频综合在线| 欧美国产综合视频| 欧美在线观看www| 一本一本久久a久久精品综合妖精 一本一本久久a久久精品综合麻豆 | 亚洲精品乱码久久久久久日本蜜臀| 亚洲欧美中文日韩v在线观看| 在线成人小视频| 国产精品人人做人人爽| 欧美国产视频在线观看| 欧美一区二区视频在线观看2020 | 一区二区三区国产在线观看| 久久福利电影| 亚洲午夜精品一区二区| 亚洲国产色一区| 国产视频一区三区| 欧美午夜电影网| 欧美久久久久久久久| 久久久成人网| 久久av一区二区三区漫画| 一区二区三区视频观看| 亚洲精品久久久久久下一站| 欧美成人精品高清在线播放| 久久久一本精品99久久精品66| 亚洲综合色丁香婷婷六月图片| 亚洲精品自在在线观看| 亚洲国产一区二区精品专区| 黑人一区二区三区四区五区| 国产精品网站在线观看| 欧美性做爰毛片| 欧美日韩一区二区视频在线观看 | 久久婷婷蜜乳一本欲蜜臀| 欧美一区二区免费| 亚洲一区二区三区在线| 亚洲网址在线| 亚洲综合清纯丝袜自拍| 亚洲免费视频成人| 亚洲一区二区三区四区视频| 亚洲一区二区三区免费观看 | 一区二区动漫| 亚洲一级高清| 午夜精品久久久久久久久久久久 | 小嫩嫩精品导航| 午夜精品理论片| 欧美一区二区三区精品电影| 欧美一级午夜免费电影| 欧美在线一二三区| 久久蜜臀精品av| 欧美成人久久| 欧美日韩一区二区精品| 国产精品美女一区二区| 国产视频在线观看一区| 黄色国产精品| 亚洲经典三级| 亚洲一区欧美激情| 欧美在线视频一区二区| 蜜臀91精品一区二区三区| 欧美激情一区二区三区在线视频观看| 亚洲国产欧美日韩精品| 宅男噜噜噜66一区二区66| 午夜久久99| 欧美aⅴ99久久黑人专区| 欧美久久视频| 国产精品一区二区三区观看| 国内不卡一区二区三区| 亚洲乱码久久| 久久久久国产精品厨房| 亚洲国产女人aaa毛片在线| 宅男在线国产精品| 久久综合给合| 国产精品国产亚洲精品看不卡15| 韩日视频一区| 中国成人亚色综合网站| 久久久久久有精品国产| 亚洲精品一二| 久久一区亚洲| 国产精品久久精品日日| 亚洲国产精品久久精品怡红院| 亚洲一区二区高清| 欧美va天堂在线| 亚洲免费影院| 欧美精品日韩| 在线国产亚洲欧美| 香港久久久电影| 亚洲人成在线免费观看| 亚洲三级影院| 久久九九热re6这里有精品| 亚洲国产成人在线视频| 亚洲国产成人午夜在线一区| 亚洲在线观看|