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

C++分析研究  
C++
日歷
<2025年12月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
統計
  • 隨筆 - 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 閱讀(316) 評論(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>
            欧美日韩视频不卡| 欧美粗暴jizz性欧美20| 国产精品啊v在线| 中文在线资源观看视频网站免费不卡| 亚洲国产高清在线观看视频| 免费看黄裸体一级大秀欧美| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲乱码视频| 欧美日韩一区二区在线播放| 亚洲一区二区影院| 翔田千里一区二区| 亚洲国产精品成人综合色在线婷婷| 免费观看亚洲视频大全| 久久尤物电影视频在线观看| 亚洲精品黄色| 亚洲视频福利| 一区二区在线免费观看| 亚洲高清视频在线观看| 欧美日韩在线一区二区三区| 欧美一区二区三区视频免费| 久久久久在线观看| 在线综合亚洲欧美在线视频| 亚洲女优在线| 亚洲高清视频中文字幕| 亚洲美女在线视频| 韩国免费一区| 日韩一级大片| 黄色一区二区在线| 日韩写真在线| 亚洲第一福利社区| 亚洲一区二区三区四区五区午夜 | 欧美激情黄色片| 久久国产一区| 欧美片在线播放| 久久阴道视频| 国产精品久久久一区二区三区| 六月婷婷一区| 国产精品一区二区在线| 亚洲国产精品va| 国模吧视频一区| 亚洲午夜精品国产| 亚洲电影免费观看高清完整版在线观看| 日韩视频一区二区三区在线播放| 国产一级久久| 亚洲一区二区三区在线视频| 亚洲破处大片| 久久免费精品视频| 欧美专区第一页| 欧美性大战xxxxx久久久| 欧美a级片网站| 国内精品久久久久影院薰衣草| 99这里只有精品| 日韩天堂在线视频| 另类人畜视频在线| 久久免费精品视频| 国产日韩精品一区二区浪潮av| 99视频日韩| 中文国产亚洲喷潮| 欧美精品18+| 亚洲欧洲日韩女同| 日韩视频一区二区三区在线播放| 美女精品视频一区| 亚洲国产成人av好男人在线观看| 精品成人乱色一区二区| 欧美一区二区在线播放| 欧美在线视频免费观看| 国产精品视频自拍| 亚洲你懂的在线视频| 午夜精品美女自拍福到在线| 国产精品青草久久久久福利99| 在线亚洲精品| 性欧美video另类hd性玩具| 欧美性做爰毛片| 亚洲在线视频一区| 久久久国产午夜精品| 国产中文一区| 久久久国产视频91| 亚洲观看高清完整版在线观看| 亚洲黄色成人| 欧美日本免费| 亚洲一区二区高清视频| 欧美资源在线| 永久久久久久| 欧美国产日本韩| 99精品视频一区二区三区| 亚洲一二三区在线| 国产手机视频精品| 毛片基地黄久久久久久天堂| 亚洲黄色三级| 亚洲欧美日韩人成在线播放| 国产美女搞久久| 可以看av的网站久久看| 亚洲人成在线播放| 欧美亚洲免费高清在线观看| 国语自产偷拍精品视频偷| 你懂的网址国产 欧美| 亚洲伦伦在线| 久久久久久精| 99综合在线| 国产亚洲精品aa| 欧美黑人一区二区三区| 一本到高清视频免费精品| 香港久久久电影| 黄色一区二区三区| 欧美久久久久久久| 午夜欧美精品| 亚洲精品黄色| 老司机一区二区三区| 一区二区三区 在线观看视| 国产一区香蕉久久| 欧美日韩国产经典色站一区二区三区| 欧美一区1区三区3区公司| 亚洲国产视频a| 久久免费视频这里只有精品| 亚洲视频在线观看一区| 亚洲大胆在线| 国产精品一区二区久激情瑜伽| 免费欧美在线视频| 欧美一区二区三区精品| 99精品99| 亚洲经典在线| 欧美v国产在线一区二区三区| 亚洲欧美春色| 亚洲视频一区二区在线观看| 在线看片第一页欧美| 国产精自产拍久久久久久| 欧美精品在线播放| 久久综合九色九九| 久久久999精品视频| 亚洲小视频在线观看| 99热这里只有成人精品国产| 亚洲高清二区| 欧美电影在线观看| 六十路精品视频| 久久婷婷一区| 久久免费精品视频| 久久久久久网站| 久久福利电影| 久久福利资源站| 欧美一区二区三区的| 午夜国产欧美理论在线播放| 亚洲一区二区三区精品在线| 99国产精品| 在线亚洲美日韩| 亚洲午夜一区| 午夜欧美理论片| 欧美中文在线观看| 久久精品国产一区二区三| 久久福利毛片| 久久天堂精品| 免播放器亚洲一区| 欧美黑人多人双交| 亚洲国产91| 亚洲精品一区二区三区在线观看 | 亚洲特级毛片| 亚洲一区二区三区影院| 亚洲一区二区三区四区在线观看| 亚洲专区一区二区三区| 午夜日韩在线观看| 久久成人一区| 久久综合狠狠| 亚洲电影有码| 日韩视频一区二区在线观看| 9i看片成人免费高清| 亚洲欧美日韩一区二区三区在线观看| 亚洲在线电影| 久久综合伊人77777麻豆| 欧美成人久久| 国产精品视频一二三| 在线观看成人av电影| 日韩午夜在线电影| 欧美在线免费观看视频| 美日韩丰满少妇在线观看| 亚洲国产精品欧美一二99| 99在线热播精品免费99热| 久久av一区二区三区漫画| 欧美激情一二三区| 国产老肥熟一区二区三区| 亚洲电影在线| 亚洲免费一在线| 欧美国产精品中文字幕| 9l国产精品久久久久麻豆| 久久精品国产精品亚洲| 欧美日韩国产首页| 国内精品视频在线播放| 一本久道久久综合狠狠爱| 久久精品国产综合| 亚洲激情视频| 久久久久se| 欧美特黄一区| 亚洲激情二区| 久久久久久久久久看片| 日韩午夜三级在线| 麻豆成人综合网| 国产欧美日韩亚州综合| 99精品热6080yy久久| 久久亚洲精品网站| 亚洲欧美日本国产专区一区| 免费看成人av| 在线观看日韩国产|