• <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>
            C++分析研究  
            C++
            日歷
            <2013年2月>
            272829303112
            3456789
            10111213141516
            17181920212223
            242526272812
            3456789
            統(tǒng)計(jì)
            • 隨筆 - 92
            • 文章 - 4
            • 評(píng)論 - 4
            • 引用 - 0

            導(dǎo)航

            常用鏈接

            留言簿

            隨筆檔案

            文章檔案

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

             
              我曾經(jīng)自學(xué)過C++,現(xiàn)在回想起來(lái),當(dāng)時(shí)是什么都不懂。說不上能使用C++,倒是被C++牽著鼻子走了。高中搞NOIP并不允許使用STL庫(kù),比賽中C++面向?qū)ο蟮臋C(jī)制基本沒有什么用武之地,所以高中搞NOIP名為用C++,其實(shí)就是c加上了cout和cin。
             
               前幾天看韓老師的《老碼識(shí)途》,里面記錄了一些C++面向?qū)ο髾C(jī)制的探索,又勾起了我的興趣。而這個(gè)學(xué)期自學(xué)了匯編,又給了我自己動(dòng)手探索提供了能力基礎(chǔ),自己上手以后,從一個(gè)更加底層的視角看C++機(jī)制的實(shí)現(xiàn),讓我在黑暗中摸到了馴服C++的韁繩。
             
               引用:
             
               本質(zhì)上是指針,這一點(diǎn)即使大家沒有看反匯編應(yīng)該也是猜到了。
             
               對(duì)象在內(nèi)存上的布局:
             
               1: class Father
             
               2: {
             
               3: int iA_;
             
               4: int iB_;
             
               5:
             
               6: void FuncA();
             
               7: void FuncB();
             
               8: };
             
               9:
             
               10: class Child : Father
             
               11: {
             
               12: int iC_;
             
               13: void FuncC();
             
               14: };
             
               一個(gè)Father對(duì)象里只包含 (低地址 –> 高地址) : iA_,iB_。也就是一個(gè)Father對(duì)象的大小是8個(gè)字節(jié),函數(shù)并不會(huì)占用內(nèi)存空間托福答案
             
               為什么不會(huì)?
             
               其實(shí)類的成員函數(shù)可以看做本質(zhì)上與普通函數(shù)相同。
             
               編譯器在編譯的時(shí)候就知道函數(shù)的位置,所以調(diào)用普通函數(shù)的時(shí)候會(huì)直接 call 函數(shù)地址(偏移)。也就是被硬編碼了,函數(shù)的地址是固定的( 不考慮重定位之類的情況 )。
             
               而成員函數(shù)的調(diào)用也是如此,只是編譯器還多做了一件事情,就是判斷這個(gè)對(duì)象有沒有調(diào)用這個(gè)函數(shù)的“權(quán)限”(函數(shù)不是你聲明的,當(dāng)然無(wú)權(quán)調(diào)用),“權(quán)限”不夠就會(huì)報(bào)錯(cuò),告訴那個(gè)對(duì)象類型沒有這個(gè)方法。
             
               所以,類對(duì)象的大小與這個(gè)類的方法數(shù)多少是沒關(guān)系的。成員函數(shù)和普通函數(shù)本質(zhì)上一樣,實(shí)現(xiàn)這個(gè)機(jī)制,要靠編譯器來(lái)做工作雅思答案
             
               this指針:
             
               成員函數(shù)與普通函數(shù)不同之處之一就是訪問對(duì)象的數(shù)據(jù)。
             
               要訪問一個(gè)對(duì)象的元素,說白了就是要找到這個(gè)元素所在的內(nèi)存位置,也就是要有指針。
             
               我們沒有看到傳遞this指針,因?yàn)檫@件事又是編譯器幫我們做了。
             
               反匯編會(huì)看到對(duì)象調(diào)用一個(gè)方法的時(shí)候,會(huì)將這個(gè)對(duì)象的首部地址賦值給ecx寄存器,通過寄存器來(lái)傳遞this指針。
             
               我們?cè)诔蓡T函數(shù)里可以不需明寫this指針地調(diào)用對(duì)象元素,還是因?yàn)榫幾g器幫我們多做了一步“翻譯”。
             
               私有化:
             
               不多說,就是編譯器在編譯階段通過源碼來(lái)判斷某個(gè)元素是不是能夠被訪問,某個(gè)方法是不是能夠被調(diào)用,運(yùn)行的時(shí)候并不會(huì)有訪問限制。看代碼:
             
               1: #include <stdio.h>
             
               2:
             
               3: class Exp
             
               4: {
             
               5: int iA_;
             
               6: int iB_;
             
               7:
             
               8: public:
             
               9: Exp()
             
               10: {
             
               11: iA_ = iB_ = 0;
             
               12: }
             
               13: void Out()
             
               14: {
             
               15: printf("%d \t %d \n",iA_,iB_);
             
               16: }
             
               17: };
             
               18:
             
               19: int main()
             
               20: {
             
               21: Exp oA;
             
               22: void *pC = &oA;
             
               23:
             
               24: oA.Out();
             
               25: *(int*)pC = 1;
             
               26: *(int*)((int)pC+4) = 2;
             
               27: oA.Out();
             
               28:
             
               29: return 0;
             
               30: }
             
               結(jié)果是: 0 0
             
               1 2
             
               雖然 iA_,iB_是私有的,但是還是被外界修改了。因?yàn)榫幾g器無(wú)法知道我干了這事(顯式的 oA.iA_ = 1 就被發(fā)現(xiàn)了哈)
             
               構(gòu)造與析構(gòu):
             
               說道底還是編譯器幫我們?cè)诙嘧隽艘恍┕ぷ鳎闪艘恍╊~外代碼。
             
               需要注意的是:
             
               1: void Test( Father oP )
             
               2: {
             
               3: }
             
               4:
             
               5: int main()
             
               6: {
             
               7: Father oA;
             
               8: Test(oA);
             
               9: return 0;
             
               10: }
             
               會(huì)調(diào)用拷貝構(gòu)造函數(shù)。
             
               重載:
             
               一樣還是編譯器的功勞,C++最后生成的函數(shù)名是與參數(shù)有關(guān)的,所以又不同參數(shù)的函數(shù)最后生成的函數(shù)名不同,看似同名,實(shí)則不同。在函數(shù)調(diào)用的時(shí)候,編譯器會(huì)判斷參數(shù)的類型,相應(yīng)的可以生成一個(gè)函數(shù)名進(jìn)行“匹配”。( 當(dāng)然不止這么簡(jiǎn)單,還會(huì)考慮發(fā)生類型轉(zhuǎn)換的情況 )
             
               繼承:
             
               從內(nèi)存布局的角度上看
             
               1: struct Child : Father
             
               和
             
               1: struct Child
             
               2: {
             
               3: Father o;
             
               4: //other
             
               5: };
             
               相同(虛函數(shù)情況后面討論)。子類的前面部分和父類是一樣的。
             
               所以一個(gè)接受 Father * 參數(shù)的函數(shù)可以接受 Child *參數(shù),而且轉(zhuǎn)換是安全的北美托福答案
             
               有 Father & 類型參數(shù)的函數(shù)可以接受 Child &,但是繼承方式要public。But , why ?
             
               protected和private繼承模式,子類繼承的父類的接口對(duì)外都是隱藏的,所以以一個(gè)Father &傳入的參數(shù)所有的方法元素原則上是不可用的,用了肯定是違反規(guī)則的,編譯器判定這一點(diǎn),所以報(bào)錯(cuò)。
             
               虛函數(shù):
             
               比較特別的是這個(gè)。
             
               Question:為什么需要虛函數(shù)?
             
               網(wǎng)上看到的答案:基類可以通過虛函數(shù)對(duì)子類的相識(shí)功能進(jìn)行管理。(我的C++primer被借走以后就此失蹤,所以只能網(wǎng)上找了)。
             
               虛函數(shù)具體怎么回事就不細(xì)說了,討論一下背后的機(jī)制。
             
               為了能夠?qū)崿F(xiàn)虛函數(shù),每個(gè)有虛函數(shù)的類有一張對(duì)應(yīng)的虛表。這個(gè)虛表儲(chǔ)存在只讀內(nèi)存區(qū),記錄了對(duì)應(yīng)函數(shù)的地址。(PS:一個(gè)類就只有一個(gè)虛表)
             
               每個(gè)類對(duì)象都要保存一個(gè)虛表指針,保存本類的虛表地址。所以你使用 Father *指針指向一個(gè)Child對(duì)象,調(diào)用的虛函數(shù)是Child的。
             
               虛表指針保存在每個(gè)對(duì)象的首部。
             
               1: class Child : Father
             
               2: {
             
               3: int iC_;
             
               4: void FuncC();
             
               5: virtual void VF();
             
               6: };
             
               現(xiàn)在這個(gè)Child對(duì)象較前面的多了四個(gè)字節(jié)。內(nèi)存布局(從低地址到高地址)是:虛表指針__vfptr,iA_,iB_,iC_。
             
               好。問題來(lái)了,Child繼承了Father,但是Father的函數(shù)并沒有為Child再量身定做一次,也就是說無(wú)論是Father對(duì)象還是Child對(duì)象,他們調(diào)用FuncA()都是同一個(gè)函數(shù)。但是Father并沒有__vfptr,Child對(duì)象在頭部多了這個(gè),F(xiàn)uncA()中用this指針定位iA_和iB_不是都不正確嗎?
             
               現(xiàn)象告訴我們FuncA()是可以正確訪問iA_和iB_,所以推測(cè)Child對(duì)象在調(diào)用FuncA的時(shí)候,傳的不是真正的首部地址,而是往后偏移了四個(gè)字節(jié)托福改分
             
               反匯編,確實(shí)如此。這么說Father類里不能調(diào)用虛函數(shù)了?當(dāng)然,F(xiàn)ather都還不知道虛函數(shù)這回事,怎么在FuncA中調(diào)用。
             
               還有一個(gè)有趣的現(xiàn)象:
             
               1: #include <stdio.h>
             
               2:
             
               3: class Base
             
               4: {
             
               5: public:
             
               6: virtual void ShowID()
             
               7: {
             
               8: printf("Base\n");
             
               9: }
             
               10: };
             
               11:
             
               12: class CB : public Base
             
               13: {
             
               14: public:
             
               15: virtual void ShowID()
             
               16: {
             
               17: printf("CB\n");
             
               18: }
             
               19: };
             
               20:
             
               21: class CC : public Base
             
               22: {
             
               23: public:
             
               24: virtual void ShowID()
             
               25: {
             
               26: printf("CC\n");
             
               27: }
             
               28: };
             
               29:
             
               30: void Test( CB& oB )
             
               31: {
             
               32: oB.ShowID();
             
               33: }
             
               34:
             
               35: int main()
             
               36: {
             
               37: Base oBase;
             
               38: CB oB;
             
               39: CC oC;
             
               40:
             
               41: CB* pCB = &oB;
             
               42:
             
               43: *(int*)(&oB) = *(int*)(&oC); //修改虛表指針
             
               44: oB.ShowID();
             
               45: ((CB*)(&oB))->ShowID();
             
               46: pCB->ShowID();
             
               47: Test(oB);
             
               48:
             
               49: return 0;
             
               50: }
             
               猜猜結(jié)果啊,買定離手。
             
               結(jié)果是:CB CB CC CC
             
               在43行的地方,修改了oB的虛表指針,讓其指向CC類的虛表。
             
               但是oB.ShowID()沒理會(huì)我們的修改,還是調(diào)用CB類的ShowID。反匯編,發(fā)現(xiàn)他沒走“獲取虛表指針,在虛表中得到相應(yīng)的函數(shù)地址”這一套,直接調(diào)用了。因?yàn)橐话闳瞬粫?huì)閑著蛋疼去改對(duì)象的虛表指針的,對(duì)象的類型是明確的,編譯器可以通過這些信息確定調(diào)用的函數(shù)地址,所以沒必要走他一套,這樣效率還更高托福答案
             
               而pCB->ShowID()就不同了,他很乖地地走了流程,因?yàn)橐粋€(gè)父類指針可以指向一個(gè)子類對(duì)象,編譯器無(wú)法找信息,所以走流程托福改分
             
               那現(xiàn)在糾結(jié)了,為神馬 ((CB*)(&oB))->ShowID() 輸出CB。
             
               反匯編看,發(fā)現(xiàn)編譯器又擅自做主,沒有走指針的流程。
             
               那你猜猜((Base*)(&oB))->ShowID();輸出的是什么?CC。
             
               比較二者的差異,可以大概發(fā)現(xiàn)一些端倪,什么時(shí)候走流程,什么時(shí)候不走。
             
               最后是Test(oB)了,前面說過引用的本質(zhì)是指針,所以這個(gè)結(jié)果很好理解。
             
               還有,想過
             
               1: void Test2( Base oP )
             
               2: {
             
               3: oP.ShowID();
             
               4: }
             
               拷貝的時(shí)候有沒有拷貝虛表指針嗎?試試就知道,厄…發(fā)現(xiàn)沒有。
             
               前面說過這樣會(huì)調(diào)用拷貝構(gòu)造函數(shù),但是你在這個(gè)函數(shù)你沒有寫虛表指針的賦值。但是邪惡的編譯器已經(jīng)幫你悄悄加上去了哈哈哈哈~。(唉?節(jié)操呢)
             
               RTTI
             
               每個(gè)類有特定的虛表地址,每個(gè)對(duì)象會(huì)保存這個(gè)虛表地址,應(yīng)該想到了吧,偷懶,不寫了。
             
               綜上。可以看到,面向?qū)ο髾C(jī)制在底層并不特別,機(jī)制的實(shí)現(xiàn)主要靠的是編譯器。
             
            posted on 2013-04-16 11:42 HAOSOLA 閱讀(307) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


             
            Copyright © HAOSOLA Powered by: 博客園 模板提供:滬江博客
            PK10開獎(jiǎng) PK10開獎(jiǎng)
            国产精品免费久久久久影院| 久久久久99这里有精品10 | 久久香蕉一级毛片| 日韩AV无码久久一区二区 | 中文字幕乱码久久午夜| 亚洲精品国产综合久久一线| 久久最新免费视频| 久久亚洲精品无码aⅴ大香| 亚洲国产综合久久天堂| 久久久高清免费视频| 久久SE精品一区二区| 久久国产欧美日韩精品| 精品综合久久久久久97超人| 久久久无码一区二区三区| 久久精品国产91久久综合麻豆自制 | 色婷婷综合久久久久中文| 亚洲国产精品无码久久一区二区 | 欧洲成人午夜精品无码区久久| 亚洲午夜久久久影院伊人| 99久久99久久久精品齐齐| 国产成人久久精品二区三区| 香蕉aa三级久久毛片| 日韩精品久久久久久久电影蜜臀| 久久久久久久99精品免费观看| 久久青青草原精品影院| 久久久91人妻无码精品蜜桃HD | 久久国产免费观看精品3| 色综合久久综合网观看| 7777精品伊人久久久大香线蕉| 久久香蕉国产线看观看精品yw| 精品久久久久久久久久中文字幕 | 久久这里只有精品18| 国产精品毛片久久久久久久| 久久亚洲AV无码西西人体| 久久青青草原亚洲av无码app | 国产国产成人久久精品| 久久久无码精品亚洲日韩蜜臀浪潮 | 久久99这里只有精品国产| 久久久久免费精品国产| 国产精品久久久久久久人人看| 狠狠色婷婷久久一区二区三区|