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

            大龍的博客

            常用鏈接

            統(tǒng)計

            最新評論

            多態(tài)性----vptr----vtable

            多態(tài)性 (polymorphism) 是面向?qū)ο缶幊痰幕咎卣髦弧6?C++ 中,多態(tài)性通過虛函數(shù) (virtual function) 來實現(xiàn)。我們來看一段簡單的代碼:
              #include <iostream>
              using namespace std;
              class Base
              {
              int a;
              public:
              virtual void fun1() {cout<<"Base::fun1()"<<endl;}
              virtual void fun2() {cout<<"Base::fun2()"<<endl;}
              virtual void fun3() {cout<<"Base::fun3()"<<endl;}
              };
              class A:public Base
              {
              int a;
              public:
              void fun1() {cout<<"A::fun1()"<<endl;}
              void fun2() {cout<<"A::fun2()"<<endl;}
              };
              void foo (Base& obj)
              {
              obj.fun1();
              obj.fun2();
              obj.fun3();
              }
              int main()
              {
              Base b;
              A a;
              foo(b);
              foo(a);
              }
              運行結(jié)果為:
              Base::fun1()
              Base::fun2()
              Base::fun3()
              A::fun1()
              A::fun2()
              Base::fun3()
              僅通過基類的接口,程序調(diào)用了正確的函數(shù),它就好像知道我們輸入的對象的類型一樣!
              那么,編譯器是如何知道正確代碼的位置的呢?其實,編譯器在編譯時并不知道要調(diào)用的函數(shù)體的正確位置,但它插入了一段能找到正確的函數(shù)體的代碼。這稱之為 晚捆綁 (late binding) 或 運行時捆綁 (runtime binding) 技術(shù)。
              通過virtual 關(guān)鍵字創(chuàng)建虛函數(shù)能引發(fā)晚捆綁,編譯器在幕后完成了實現(xiàn)晚捆綁的必要機制。它對每個包含虛函數(shù)的類創(chuàng)建一個表(稱為VTABLE),用于放置虛函數(shù)的地址。在每個包含虛函數(shù)的類中,編譯器秘密地放置了一個稱之為vpointer(縮寫為VPTR)的指針,指向這個對象的VTABLE。所以無論這個對象包含一個或是多少虛函數(shù),編譯器都只放置一個VPTR即可。VPTR由編譯器在構(gòu)造函數(shù)中秘密地插入的代碼來完成初始化,指向相應的VTABLE,這樣對象就“知道”自己是什么類型了。 VPTR都在對象的相同位置,常常是對象的開頭。這樣,編譯器可以容易地找到對象的VTABLE并獲取函數(shù)體的地址。
              如果我們用sizeof查看前面Base類的長度,我們就會發(fā)現(xiàn),它的長度不僅僅是一個int的長度,而是增加了剛好是一個void指針的長度(在我的機器里面,一個int占4個字節(jié),一個void指針占4個字節(jié),這樣正好類Base的長度為8個字節(jié))。
              每當創(chuàng)建一個包含虛函數(shù)的類或從包含虛函數(shù)的類派生一個類時,編譯器就為這個類創(chuàng)建一個唯一的VTABLE。在VTABLE中,放置了這個類中或是它的基類中所有虛函數(shù)的地址,這些虛函數(shù)的順序都是一樣的,所以通過偏移量可以容易地找到所需的函數(shù)體的地址。假如在派生類中沒有對在基類中的某個虛函數(shù)進行重寫(overriding),那末還使用基類的這個虛函數(shù)的地址(正如上面的程序結(jié)果所示)。
              

              至今為止,一切順利。下面,我們的試驗開始了。
              就目前得知的,我們可以試探著通過自己的代碼來調(diào)用虛函數(shù),也就是說我們要找尋一下編譯器秘密地插入的那段能找到正確函數(shù)體的代碼的足跡。
              如果我們有一個Base指針作為接口,它一定指向一個Base或由Base派生的對象,或者是A,或者是其它什么。這無關(guān)緊要,因為VPTR的位置都一樣,一般都在對象的開頭。如果是這樣的話,那么包含有虛函數(shù)的對象的指針,例如Base指針,指向的位置恰恰是另一個指針——VPTR。VPTR指向的 VTABLE其實就是一個函數(shù)指針的數(shù)組,現(xiàn)在,VPTR正指向它的第一個元素,那是一個函數(shù)指針。如果VPTR向后偏移一個Void指針長度的話,那么它應該指向了VTABLE中的第二個函數(shù)指針了。
              這看來就像是一個指針連成的鏈,我們得從當前指針獲取它指向的下一個指針,這樣我們才能“順藤摸瓜”。那么,我來介紹一個函數(shù):
              void *getp (void* p)
              {
              return (void*)*(unsigned long*)p;
              }
              我們不考慮它漂亮與否,我們只是試驗。getp() 可以從當前指針獲取它指向的下一個指針。如果我們能找到函數(shù)體的地址,用什么來存儲它呢?我想應該用一個函數(shù)指針:
              typedef void (*fun)();
              它與Base中的三個虛函數(shù)相似,為了簡單我們不要任何輸入和返回,我們只要知道它實際上被執(zhí)行了即可。
              然后,我們負責“摸瓜”的函數(shù)登場了:
              fun getfun (Base* obj, unsigned long off)
              {
              void *vptr = getp(obj);
              unsigned char *p = (unsigned char *)vptr;
              p += sizeof(void*) * off;
              return (fun)getp(p);
              }
              第一個參數(shù)是Base指針,我們可以輸入Base或是Base派生對象的指針。第二個參數(shù)是VTABLE偏移量,偏移量如果是0那么對應fun1(),如果是1對應fun2()。getfun() 返回的是fun類型函數(shù)指針,我們上面定義的那個。可以看到,函數(shù)首先就對Base指針調(diào)用了一次getp(),這樣得到了vptr這個指針,然后用一個 unsigned char指針運算偏移量,得到的結(jié)果再次輸入getp(),這次得到的就應該是正確的函數(shù)體的位置了。
              那么它到底能不能正確工作呢?我們修改main() 來測試一下:
              int main()
              {
              Base *p = new A;
              fun f = getfun(p, 0);
              (*f)();
              f = getfun(p, 1);
              (*f)();
              f = getfun(p, 2);
              (*f)();
              delete p;
              }
              激動人心的時刻到來了,讓我們運行它!
              運行結(jié)果為:
              A::fun1()
              A::fun2()
              Base::fun3()
              至此,我們真的成功了。通過我們的方法,我們獲取了對象的VPTR,在它的體外執(zhí)行了它的虛函數(shù)。

            posted on 2007-01-25 20:44 大龍 閱讀(4583) 評論(3)  編輯 收藏 引用

            評論

            # re: 多態(tài)性----vptr----vtable 2008-05-08 10:33 nitrotoluene

            兄弟,受教了!  回復  更多評論   

            # re: 多態(tài)性----vptr----vtable 2008-11-29 20:49 langzilingqi

            了解了VPTR,反而又更深入地了解了虛函數(shù)的實現(xiàn)了,多謝!  回復  更多評論   

            # re: 多態(tài)性----vptr----vtable 2015-05-05 20:41 mudongliang

            非常好,學習了!  回復  更多評論   

            成人a毛片久久免费播放| 嫩草影院久久99| 狠狠色丁香婷婷久久综合| 久久久无码精品亚洲日韩京东传媒 | 一级a性色生活片久久无少妇一级婬片免费放| 亚洲一区二区三区日本久久九| 久久精品国产只有精品66| 久久国产AVJUST麻豆| 精品综合久久久久久888蜜芽| 国产免费久久精品丫丫| 亚洲精品白浆高清久久久久久| 嫩草影院久久99| 久久婷婷激情综合色综合俺也去| 99久久国产免费福利| 久久婷婷国产综合精品| 伊人久久大香线蕉综合网站| 成人国内精品久久久久影院| 久久99精品久久久大学生| 国产香蕉97碰碰久久人人| 精品久久久久久久无码| 久久露脸国产精品| 亚洲国产成人久久综合一| 久久夜色精品国产噜噜噜亚洲AV| 久久久99精品成人片中文字幕| 国产亚洲精品美女久久久| 亚洲国产成人久久一区久久| 久久综合久久综合久久综合| 东京热TOKYO综合久久精品| 狠狠色丁香婷婷久久综合| 香蕉aa三级久久毛片| 久久久久人妻精品一区三寸蜜桃| 国产精品综合久久第一页| 久久99精品国产99久久| 久久精品嫩草影院| 精品久久久久久亚洲| 久久精品一区二区国产| 好久久免费视频高清| 9久久9久久精品| 99热都是精品久久久久久| 久久精品国产亚洲Aⅴ香蕉 | 久久久久99精品成人片三人毛片 |