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

C++分析研究  
C++
日歷
<2013年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011
統計
  • 隨筆 - 92
  • 文章 - 4
  • 評論 - 4
  • 引用 - 0

導航

常用鏈接

留言簿

隨筆檔案

文章檔案

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

 
  我曾經自學過C++,現在回想起來,當時是什么都不懂。說不上能使用C++,倒是被C++牽著鼻子走了。高中搞NOIP并不允許使用STL庫,比賽中C++面向對象的機制基本沒有什么用武之地,所以高中搞NOIP名為用C++,其實就是c加上了cout和cin。
 
   前幾天看韓老師的《老碼識途》,里面記錄了一些C++面向對象機制的探索,又勾起了我的興趣。而這個學期自學了匯編,又給了我自己動手探索提供了能力基礎,自己上手以后,從一個更加底層的視角看C++機制的實現,讓我在黑暗中摸到了馴服C++的韁繩。
 
   引用:
 
   本質上是指針,這一點即使大家沒有看反匯編應該也是猜到了。
 
   對象在內存上的布局:
 
   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: };
 
   一個Father對象里只包含 (低地址 –> 高地址) : iA_,iB_。也就是一個Father對象的大小是8個字節,函數并不會占用內存空間托福答案
 
   為什么不會?
 
   其實類的成員函數可以看做本質上與普通函數相同。
 
   編譯器在編譯的時候就知道函數的位置,所以調用普通函數的時候會直接 call 函數地址(偏移)。也就是被硬編碼了,函數的地址是固定的( 不考慮重定位之類的情況 )。
 
   而成員函數的調用也是如此,只是編譯器還多做了一件事情,就是判斷這個對象有沒有調用這個函數的“權限”(函數不是你聲明的,當然無權調用),“權限”不夠就會報錯,告訴那個對象類型沒有這個方法。
 
   所以,類對象的大小與這個類的方法數多少是沒關系的。成員函數和普通函數本質上一樣,實現這個機制,要靠編譯器來做工作雅思答案
 
   this指針:
 
   成員函數與普通函數不同之處之一就是訪問對象的數據。
 
   要訪問一個對象的元素,說白了就是要找到這個元素所在的內存位置,也就是要有指針。
 
   我們沒有看到傳遞this指針,因為這件事又是編譯器幫我們做了。
 
   反匯編會看到對象調用一個方法的時候,會將這個對象的首部地址賦值給ecx寄存器,通過寄存器來傳遞this指針。
 
   我們在成員函數里可以不需明寫this指針地調用對象元素,還是因為編譯器幫我們多做了一步“翻譯”。
 
   私有化:
 
   不多說,就是編譯器在編譯階段通過源碼來判斷某個元素是不是能夠被訪問,某個方法是不是能夠被調用,運行的時候并不會有訪問限制。看代碼:
 
   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: }
 
   結果是: 0 0
 
   1 2
 
   雖然 iA_,iB_是私有的,但是還是被外界修改了。因為編譯器無法知道我干了這事(顯式的 oA.iA_ = 1 就被發現了哈)
 
   構造與析構:
 
   說道底還是編譯器幫我們在多做了一些工作,生成了一些額外代碼。
 
   需要注意的是:
 
   1: void Test( Father oP )
 
   2: {
 
   3: }
 
   4:
 
   5: int main()
 
   6: {
 
   7: Father oA;
 
   8: Test(oA);
 
   9: return 0;
 
   10: }
 
   會調用拷貝構造函數。
 
   重載:
 
   一樣還是編譯器的功勞,C++最后生成的函數名是與參數有關的,所以又不同參數的函數最后生成的函數名不同,看似同名,實則不同。在函數調用的時候,編譯器會判斷參數的類型,相應的可以生成一個函數名進行“匹配”。( 當然不止這么簡單,還會考慮發生類型轉換的情況 )
 
   繼承:
 
   從內存布局的角度上看
 
   1: struct Child : Father
 
   和
 
   1: struct Child
 
   2: {
 
   3: Father o;
 
   4: //other
 
   5: };
 
   相同(虛函數情況后面討論)。子類的前面部分和父類是一樣的。
 
   所以一個接受 Father * 參數的函數可以接受 Child *參數,而且轉換是安全的北美托福答案
 
   有 Father & 類型參數的函數可以接受 Child &,但是繼承方式要public。But , why ?
 
   protected和private繼承模式,子類繼承的父類的接口對外都是隱藏的,所以以一個Father &傳入的參數所有的方法元素原則上是不可用的,用了肯定是違反規則的,編譯器判定這一點,所以報錯。
 
   虛函數:
 
   比較特別的是這個。
 
   Question:為什么需要虛函數?
 
   網上看到的答案:基類可以通過虛函數對子類的相識功能進行管理。(我的C++primer被借走以后就此失蹤,所以只能網上找了)。
 
   虛函數具體怎么回事就不細說了,討論一下背后的機制。
 
   為了能夠實現虛函數,每個有虛函數的類有一張對應的虛表。這個虛表儲存在只讀內存區,記錄了對應函數的地址。(PS:一個類就只有一個虛表)
 
   每個類對象都要保存一個虛表指針,保存本類的虛表地址。所以你使用 Father *指針指向一個Child對象,調用的虛函數是Child的。
 
   虛表指針保存在每個對象的首部。
 
   1: class Child : Father
 
   2: {
 
   3: int iC_;
 
   4: void FuncC();
 
   5: virtual void VF();
 
   6: };
 
   現在這個Child對象較前面的多了四個字節。內存布局(從低地址到高地址)是:虛表指針__vfptr,iA_,iB_,iC_。
 
   好。問題來了,Child繼承了Father,但是Father的函數并沒有為Child再量身定做一次,也就是說無論是Father對象還是Child對象,他們調用FuncA()都是同一個函數。但是Father并沒有__vfptr,Child對象在頭部多了這個,FuncA()中用this指針定位iA_和iB_不是都不正確嗎?
 
   現象告訴我們FuncA()是可以正確訪問iA_和iB_,所以推測Child對象在調用FuncA的時候,傳的不是真正的首部地址,而是往后偏移了四個字節托福改分
 
   反匯編,確實如此。這么說Father類里不能調用虛函數了?當然,Father都還不知道虛函數這回事,怎么在FuncA中調用。
 
   還有一個有趣的現象:
 
   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: }
 
   猜猜結果啊,買定離手。
 
   結果是:CB CB CC CC
 
   在43行的地方,修改了oB的虛表指針,讓其指向CC類的虛表。
 
   但是oB.ShowID()沒理會我們的修改,還是調用CB類的ShowID。反匯編,發現他沒走“獲取虛表指針,在虛表中得到相應的函數地址”這一套,直接調用了。因為一般人不會閑著蛋疼去改對象的虛表指針的,對象的類型是明確的,編譯器可以通過這些信息確定調用的函數地址,所以沒必要走他一套,這樣效率還更高托福答案
 
   而pCB->ShowID()就不同了,他很乖地地走了流程,因為一個父類指針可以指向一個子類對象,編譯器無法找信息,所以走流程托福改分
 
   那現在糾結了,為神馬 ((CB*)(&oB))->ShowID() 輸出CB。
 
   反匯編看,發現編譯器又擅自做主,沒有走指針的流程。
 
   那你猜猜((Base*)(&oB))->ShowID();輸出的是什么?CC。
 
   比較二者的差異,可以大概發現一些端倪,什么時候走流程,什么時候不走。
 
   最后是Test(oB)了,前面說過引用的本質是指針,所以這個結果很好理解。
 
   還有,想過
 
   1: void Test2( Base oP )
 
   2: {
 
   3: oP.ShowID();
 
   4: }
 
   拷貝的時候有沒有拷貝虛表指針嗎?試試就知道,厄…發現沒有。
 
   前面說過這樣會調用拷貝構造函數,但是你在這個函數你沒有寫虛表指針的賦值。但是邪惡的編譯器已經幫你悄悄加上去了哈哈哈哈~。(唉?節操呢)
 
   RTTI
 
   每個類有特定的虛表地址,每個對象會保存這個虛表地址,應該想到了吧,偷懶,不寫了。
 
   綜上??梢钥吹?,面向對象機制在底層并不特別,機制的實現主要靠的是編譯器。
 
posted on 2013-04-16 11:42 HAOSOLA 閱讀(314) 評論(0)  編輯 收藏 引用

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


 
Copyright © HAOSOLA Powered by: 博客園 模板提供:滬江博客
PK10開獎 PK10開獎
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            免费人成精品欧美精品| 欧美激情一区二区| 久久国产加勒比精品无码| 亚洲黄色av| 亚洲字幕在线观看| 欧美日韩精品久久| 亚洲三级视频| 亚洲高清视频在线观看| 欧美主播一区二区三区| 国产日韩欧美中文| 久久精彩视频| 性欧美暴力猛交另类hd| 国产欧美日韩在线| 久久电影一区| 欧美伊久线香蕉线新在线| 国产偷国产偷亚洲高清97cao| 亚洲一级黄色av| 亚洲影院免费观看| 国产日韩一区二区三区| 久久婷婷丁香| 久久久999精品| 亚洲精品视频在线看| 亚洲精品国产欧美| 嫩草影视亚洲| 一区二区三区久久精品| 亚洲一区二区精品在线观看| 国产精品永久| 欧美.日韩.国产.一区.二区| 欧美精品1区| 亚洲欧美日韩在线综合| 久久狠狠亚洲综合| 亚洲人成网站精品片在线观看| 亚洲国产精品电影| 国产精品二区二区三区| 久久久久国产精品厨房| 欧美不卡视频| 午夜精品福利视频| 久久久久国产一区二区三区| 亚洲精品日产精品乱码不卡| 亚洲日韩欧美一区二区在线| 国产精品毛片a∨一区二区三区| 久久久久女教师免费一区| 欧美成人精品一区二区三区| 久久天堂成人| 一本久久a久久精品亚洲| 亚洲一区在线播放| 亚洲观看高清完整版在线观看| 亚洲黄色在线视频| 国产九九精品| 亚洲丰满少妇videoshd| 欧美午夜视频一区二区| 久久精品一区| 国产日韩欧美中文在线播放| 亚洲自拍另类| 午夜精品久久一牛影视| 亚洲精品视频免费在线观看| 校园激情久久| 国产精品99久久久久久白浆小说| 午夜在线视频一区二区区别| 亚洲二区三区四区| 亚洲欧美综合精品久久成人| a91a精品视频在线观看| 久久久久久久久久久成人| 亚洲欧美日韩国产| 欧美精品91| 欧美国产日韩免费| 国产一区二区三区免费观看| 正在播放亚洲一区| 99视频一区二区| 玖玖在线精品| 久热这里只精品99re8久| 国产精品一区亚洲| 中日韩视频在线观看| av成人免费在线观看| 另类春色校园亚洲| 免费亚洲视频| 伊人久久亚洲热| 久久久99免费视频| 久久免费视频一区| 国产揄拍国内精品对白 | 亚洲字幕在线观看| 亚洲一级二级| 欧美午夜精品久久久久久超碰| 亚洲第一页自拍| 亚洲欧洲一区二区三区在线观看| 久久av一区| 久久久亚洲精品一区二区三区| 国产精品五月天| 亚洲欧美日韩国产一区| 亚洲综合成人在线| 国产精品人人爽人人做我的可爱| 国产精品99久久久久久有的能看| 中文在线资源观看网站视频免费不卡 | 蜜臀久久99精品久久久画质超高清 | 在线欧美日韩国产| 久久久精品999| 久色婷婷小香蕉久久| 在线观看视频一区二区| 久久天天综合| 亚洲国产一区二区a毛片| 欧美日韩亚洲一区二区三区在线观看 | 国产亚洲精品综合一区91| 亚洲精品免费在线| 在线视频欧美一区| 欧美色图一区二区三区| 久久高清国产| 蜜臀久久久99精品久久久久久 | 亚洲欧洲一区二区天堂久久| 久久亚洲综合| 91久久精品美女| 亚洲综合色网站| 国产亚洲欧美日韩在线一区| 久久久91精品国产一区二区三区 | 亚洲欧美国产日韩天堂区| 国产精品一区久久久| 欧美在线网址| 91久久精品国产91久久性色| 亚洲伊人网站| 黄色成人片子| 欧美伦理a级免费电影| 亚洲午夜影视影院在线观看| 久久久久久久国产| 亚洲精品一区中文| 国产美女高潮久久白浆| 久久综合99re88久久爱| 夜夜嗨av一区二区三区免费区| 欧美在线不卡视频| 亚洲精品久久视频| 国产精品一区2区| 欧美二区视频| 亚洲欧美日韩天堂| 亚洲国产清纯| 久久人人97超碰精品888| 这里只有精品视频在线| 伊人久久婷婷色综合98网| 欧美午夜a级限制福利片| 久久久久久久999| 中国日韩欧美久久久久久久久| 久久午夜电影网| 亚洲一区免费网站| 亚洲精品影院在线观看| 黑人中文字幕一区二区三区| 欧美午夜不卡在线观看免费 | 欧美亚洲视频一区二区| 亚洲电影在线观看| 久久精品亚洲| 亚洲欧美国产精品va在线观看| 91久久久亚洲精品| 激情丁香综合| 国产日韩欧美在线播放| 欧美私人网站| 欧美巨乳在线观看| 免费视频一区二区三区在线观看| 一区二区三区偷拍| 亚洲开发第一视频在线播放| 韩国三级电影一区二区| 欧美日韩一区二区三区在线| 久久久久久久久久码影片| 亚洲免费影院| 亚洲一区免费观看| 一区二区三区四区国产精品| 欧美激情一区二区三级高清视频| 久久精品日韩| 久久精品国产99国产精品澳门| 亚洲欧美高清| 亚洲欧美日韩综合aⅴ视频| 一区二区三区四区在线| 日韩亚洲视频在线| 日韩性生活视频| 亚洲美女免费视频| 亚洲三级电影全部在线观看高清 | 9国产精品视频| 91久久精品一区二区三区| 欧美激情亚洲自拍| 欧美成人免费网| 欧美成人中文字幕在线| 欧美成人精品在线播放| 裸体女人亚洲精品一区| 欧美91视频| 亚洲国产精品成人精品| 亚洲国产导航| 日韩一区二区高清| 亚洲一区二区三区免费在线观看| 亚洲午夜在线观看| 欧美一区三区三区高中清蜜桃| 性做久久久久久| 久久一二三国产| 欧美日韩成人精品| 国产精品欧美在线| 国语自产精品视频在线看一大j8 | 欧美综合二区| 久久综合一区二区| 欧美国产日韩在线观看| 欧美日韩午夜在线视频| 国产精品日韩专区| 在线精品视频一区二区三四| 亚洲精品久久久久中文字幕欢迎你| 一区二区三区四区国产精品| 欧美在线高清视频| 欧美黄色成人网|