• <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>

            以題論道----關(guān)于虛函數(shù)的一些解讀

                                                peakflys原創(chuàng)作品,轉(zhuǎn)載請注明源作者和源鏈接!
                virtual function是很多公司面試題的重點考察內(nèi)容,雖然對于C++而言這是一個老生常談的話題了,但是工作中我發(fā)現(xiàn)還是有很多人理解的不透徹。
                先看下面的一個例子:
            /**
             *\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;
            }
            你認為運行后的結(jié)果是什么呢?
            下面是在我機器上的運行結(jié)果(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
            上面例子主要考察的內(nèi)容有四塊:虛函數(shù)的執(zhí)行、引用和指針的關(guān)系、函數(shù)調(diào)用過程、類型強轉(zhuǎn)后的行為。如果你能答對所有的結(jié)果,下面的內(nèi)容可以略過。
            下面我們來一一回顧一下所涉及到的這四塊內(nèi)容。
            1、虛函數(shù)的運行機理:
            虛函數(shù)是C++實現(xiàn)多態(tài)性的必要手段,它在運行時刻才決定具體該調(diào)用哪個函數(shù)。對于虛函數(shù)的完整細節(jié)實現(xiàn)標準并未給出,但是大多數(shù)編譯器廠商,包括GCC、VS的常見實現(xiàn)都是在含有虛函數(shù)的類對象起始地址增加一個虛表指針,虛表指針指向的數(shù)組空間稱之為虛表,這個數(shù)組包含了類對象的所有虛函數(shù)地址。詳細內(nèi)容大家可以參看《Inside The C++ Object Model》的Function語義學(xué)(注:這本書里有部分結(jié)論和例子運行同現(xiàn)在主流編譯器的實現(xiàn)有出入)。
            2、引用的行為
            在常見的編譯器中,引用一般都是通過指針來實現(xiàn)的,它同指針的區(qū)別就是它比指針有更多的約束,使用上有更多的限制。
            3、虛函數(shù)的調(diào)用過程:
            虛函數(shù)的調(diào)用過程通常是以下三個步驟:
            ①、參數(shù)壓棧
            ②、從虛表指針指向的虛表中找出函數(shù)的地址
            ③、調(diào)用函數(shù)。
            這些操作都是在編譯時期就確定的,所不同的是運行時刻對象不同,其對應(yīng)的虛表中函數(shù)地址自然也就是運行時真實對象的函數(shù),這也就是虛函數(shù)實現(xiàn)的本質(zhì)。
            而這個過程中,參數(shù)的入棧是對象無關(guān)的,而且是在編譯時期就確定下來的。所以上面例子中所有指針和引用所調(diào)用函數(shù)的參數(shù),都是指針和引用本身類型對應(yīng)的函數(shù)默認參數(shù),同運行時刻他們真實指向的對象內(nèi)存無關(guān)。
            4、類型強轉(zhuǎn)后的行為
            通常的類型強轉(zhuǎn)是告訴編譯器必須按照指定結(jié)構(gòu)的內(nèi)存布局來解析對應(yīng)內(nèi)存,正如上例中”Derive *pd = (Derive*)&b; “ ,編譯器就會把b對應(yīng)的內(nèi)存來當做Derive的內(nèi)存布局來解析,但是內(nèi)存里的內(nèi)容不變,所以虛函數(shù)運行正常。
            注:這種行為很危險,如果使用的內(nèi)存布局并不適合真實內(nèi)存,很可能造成訪問越界等問題,所以要格外小心強轉(zhuǎn)操作的使用!對于例子中的downcasting行為,建議使用C++提供的dynamic_cast來轉(zhuǎn)換。
            為了大家更好的理解上面的內(nèi)容,特附上使用指針和引用分別調(diào)用虛函數(shù)過程的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對象的構(gòu)造
               400b5e:   48 89 5d e8             mov    %rbx,-0x18(%rbp)             //pb指針的賦值
                pb->print();
              400b62:   48 8b 45 e8             mov    -0x18(%rbp),%rax               //pb指針指向的內(nèi)存的首地址,即Derive對象的起始地址,亦即虛表指針的地址
              400b66:   48 8b 00                mov    (%rax),%rax                        //取虛表地址
              400b69:   48 8b 00                mov    (%rax),%rax                        //取虛表中的第一項內(nèi)容(因Derive和Base只有一個虛函數(shù)),即print函數(shù)地址
              400b6c:   48 8b 55 e8             mov    -0x18(%rbp),%rdx               //this指針傳入rdx
              400b70:   be 0a 00 00 00          mov    $0xa,%esi                         //參數(shù)10入棧(可見在編譯時期就已經(jīng)確定了)
              400b75:   48 89 d7                mov    %rdx,%rdi                            //this指針借rdx傳給rdi
              400b78:   ff d0                   callq  *%rax                                     //調(diào)用虛函數(shù)(通過真實對象的虛表來確定的真正被調(diào)函數(shù))
                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                                       //以上為通過引用調(diào)用虛函數(shù)的過程,可見同指針調(diào)用的實現(xiàn)完全相同,注釋略
            通過上面的分析,相信大家應(yīng)該都能輕松的明白上面例子的運行結(jié)果,此處不再一一解讀。
                                                                     --by peakflys 15:57:06 Sunday, December 01, 2013

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

            評論

            # re: 以題論道----關(guān)于虛函數(shù)的一些解讀 2013-12-01 19:05 Richard Wei

            effective C++ item 38  回復(fù)  更多評論   

            # re: 以題論道----關(guān)于虛函數(shù)的一些解讀 2013-12-02 09:32 peakflys

            僅作例子講解@Richard Wei
              回復(fù)  更多評論   

            # re: 以題論道----關(guān)于虛函數(shù)的一些解讀 2013-12-03 11:21 zdd

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

            # re: 以題論道----關(guān)于虛函數(shù)的一些解讀 2013-12-04 09:47 NWAO

            ".....上面例子中所有指針和引用所調(diào)用函數(shù)的參數(shù),都是指針和引用本身類型對應(yīng)的函數(shù)默認參數(shù),同運行時刻他們真實指向的對象內(nèi)存無關(guān)。"

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

            # re: 以題論道----關(guān)于虛函數(shù)的一些解讀 2013-12-04 23:45 vsgoster

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

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

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

            # re: 以題論道----關(guān)于虛函數(shù)的一些解讀 2013-12-12 00:00 Hacksign

            博主的例子,有一個坑,具有默認參數(shù)的virtual函數(shù)執(zhí)行靜態(tài)綁定。所以,如果去掉參數(shù),即virtual void print(void),那么第一個print調(diào)用應(yīng)該執(zhí)行derive版本的print。  回復(fù)  更多評論   

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導(dǎo)航

            統(tǒng)計

            公告

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

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            文章檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            久久精品国产亚洲αv忘忧草| 亚洲国产精品久久久久| 久久久久亚洲AV成人网人人网站| 精品久久久一二三区| 国产亚洲精品久久久久秋霞| 国产精品视频久久久| 久久久久久青草大香综合精品| yy6080久久| 久久久久97国产精华液好用吗| 久久精品国产色蜜蜜麻豆| 久久久久久综合一区中文字幕| 思思久久99热免费精品6| 久久A级毛片免费观看| 久久午夜免费视频| 94久久国产乱子伦精品免费| 久久久久亚洲av成人网人人软件 | 少妇久久久久久被弄到高潮| 精品国产乱码久久久久久郑州公司 | 精品国产青草久久久久福利| 久久综合亚洲色一区二区三区| 亚洲精品无码久久久久久| 久久综合成人网| 久久久久国色AV免费看图片| 99久久国产综合精品麻豆| 一本色道久久综合狠狠躁| 亚洲国产精品无码久久青草| 国产一区二区精品久久岳| 一级做a爱片久久毛片| 久久免费视频网站| 久久久久亚洲AV成人片 | 日本免费一区二区久久人人澡 | 无码八A片人妻少妇久久| 久久久国产精品网站| 久久久亚洲欧洲日产国码二区| 亚洲婷婷国产精品电影人久久| 国内精品久久久久久不卡影院| 热久久这里只有精品| 久久久久免费精品国产| 99热成人精品免费久久| 久久精品无码一区二区app| 亚洲乱码日产精品a级毛片久久|