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

yehao's Blog

為什么說虛函數的效率低

虛函數聯系到多態(tài),多態(tài)聯系到繼承。所以本文中都是在繼承層次上做文章。沒了繼承,什么都沒得談。

  下面是對C++的虛函數這玩意兒的理解。

  一, 什么是虛函數(如果不知道虛函數為何物,但有急切的想知道,那你就應該從這里開始)

  簡單地說,那些被virtual關鍵字修飾的成員函數,就是虛函數。虛函數的作用,用專業(yè)術語來解釋就是實現多態(tài)性(Polymorphism),多態(tài)性是將接口與實現進行分離;用形象的語言來解釋就是實現以共同的方法,但因個體差異而采用不同的策略。下面來看一段簡單的代碼

  class A{

  public:

  void print(){ cout<<”This is A”<<endl;}

  };

  class B:public A{

  public:

  void print(){ cout<<”This is B”<<endl;}

  };

  int main(){ //為了在以后便于區(qū)分,我這段main()代碼叫做main1

  A a;

  B b;

  a.print();

  b.print();

  }

  通過class A和class B的print()這個接口,可以看出這兩個class因個體的差異而采用了不同的策略,輸出的結果也是我們預料中的,分別是This is A和This is B。但這是否真正做到了多態(tài)性呢?No,多態(tài)還有個關鍵之處就是一切用指向基類的指針或引用來操作對象。那現在就把main()處的代碼改一改。

  int main(){ //main2

  A a;

  B b;

  A* p1=&a;

  A* p2=&b;

  p1->print();

  p2->print();

  }

  運行一下看看結果,喲呵,驀然回首,結果卻是兩個This is A。問題來了,p2明明指向的是class B的對象但卻是調用的class A的print()函數,這不是我們所期望的結果,那么解決這個問題就需要用到虛函數

  class A{

  public:

  virtual void print(){ cout<<”This is A”<<endl;} //現在成了虛函數了

  };

  class B:public A{

  public:

  void print(){ cout<<”This is B”<<endl;} //這里需要在前面加上關鍵字virtual嗎?

  };

  毫無疑問,class A的成員函數print()已經成了虛函數,那么class B的print()成了虛函數了嗎?回答是Yes,我們只需在把基類的成員函數設為virtual,其派生類的相應的函數也會自動變?yōu)樘摵瘮怠K裕琧lass B的print()也成了虛函數。那么對于在派生類的相應函數前是否需要用virtual關鍵字修飾,那就是你自己的問題了。

  現在重新運行main2的代碼,這樣輸出的結果就是This is A和This is B了。

  現在來消化一下,我作個簡單的總結,指向基類的指針在操作它的多態(tài)類對象時,會根據不同的類對象,調用其相應的函數,這個函數就是虛函數。

  二, 虛函數是如何做到的(如果你沒有看過《Inside The C++ Object Model》這本書,但又急切想知道,那你就應該從這里開始)

  虛函數是如何做到因對象的不同而調用其相應的函數的呢?現在我們就來剖析虛函數。我們先定義兩個類

  class A{ //虛函數示例代碼

  public:

  virtual void fun(){cout<<1<<endl;}

  virtual void fun2(){cout<<2<<endl;}

  };

  class B:public A{

  public:

  void fun(){cout<<3<<endl;}

  void fun2(){cout<<4<<endl;}

  };

  由于這兩個類中有虛函數存在,所以編譯器就會為他們兩個分別插入一段你不知道的數據,并為他們分別創(chuàng)建一個表。那段數據叫做vptr指針,指向那個表。那個表叫做vtbl,每個類都有自己的vtbl,vtbl的作用就是保存自己類中虛函數的地址,我們可以把vtbl形象地看成一個數組,這個數組的每個元素存放的就是虛函數的地址,請看圖

  通過上圖,可以看到這兩個vtbl分別為class A和class B服務。現在有了這個模型之后,我們來分析下面的代碼

  A *p=new A;

  p->fun();

  毫無疑問,調用了A::fun(),但是A::fun()是如何被調用的呢?它像普通函數那樣直接跳轉到函數的代碼處嗎?No,其實是這樣的,首先是取出vptr的值,這個值就是vtbl的地址,再根據這個值來到vtbl這里,由于調用的函數A::fun()是第一個虛函數,所以取出vtbl第一個slot里的值,這個值就是A::fun()的地址了,最后調用這個函數。現在我們可以看出來了,只要vptr不同,指向的vtbl就不同,而不同的vtbl里裝著對應類的虛函數地址,所以這樣虛函數就可以完成它的任務。

  而對于class A和class B來說,他們的vptr指針存放在何處呢?其實這個指針就放在他們各自的實例對象里。由于class A和class B都沒有數據成員,所以他們的實例對象里就只有一個vptr指針。通過上面的分析,現在我們來實作一段代碼,來描述這個帶有虛函數的類的簡單模型。

  #include<iostream>

  using namespace std;

  //將上面“虛函數示例代碼”添加在這里

  int main(){

  void (*fun)(A*);

  A *p=new B;

  long lVptrAddr;

  memcpy(&lVptrAddr,p,4);

  memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4);

  fun(p);

  delete p;

  system("pause");

  }

  用VC或Dev-C++編譯運行一下,看看結果是不是輸出3,如果不是,那么太陽明天肯定是從西邊出來。現在一步一步開始分析

  void (*fun)(A*); 這段定義了一個函數指針名字叫做fun,而且有一個A*類型的參數,這個函數指針待會兒用來保存從vtbl里取出的函數地址

  A* p=new B; new B是向內存(內存分5個區(qū):全局名字空間,自由存儲區(qū),寄存器,代碼空間,棧)自由存儲區(qū)申請一個內存單元的地址然后隱式地保存在一個指針中.然后把這個地址附值給A類型的指針P.

  .

  long lVptrAddr; 這個long類型的變量待會兒用來保存vptr的值

  memcpy(&lVptrAddr,p,4); 前面說了,他們的實例對象里只有vptr指針,所以我們就放心大膽地把p所指的4bytes內存里的東西復制到lVptrAddr中,所以復制出來的4bytes內容就是vptr的值,即vtbl的地址

  現在有了vtbl的地址了,那么我們現在就取出vtbl第一個slot里的內容

  memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4); 取出vtbl第一個slot里的內容,并存放在函數指針fun里。需要注意的是lVptrAddr里面是vtbl的地址,但lVptrAddr不是指針,所以我們要把它先轉變成指針類型

  fun(p); 這里就調用了剛才取出的函數地址里的函數,也就是調用了B::fun()這個函數,也許你發(fā)現了為什么會有參數p,其實類成員函數調用時,會有個this指針,這個p就是那個this指針,只是在一般的調用中編譯器自動幫你處理了而已,而在這里則需要自己處理。

  delete p; 釋放由p指向的自由空間;

  system("pause"); 屏幕暫停;

  如果調用B::fun2()怎么辦?那就取出vtbl的第二個slot里的值就行了

  memcpy(&fun,reinterpret_cast<long*>(lVptrAddr+4),4); 為什么是加4呢?因為一個指針的長度是4bytes,所以加4。或者memcpy(&fun,reinterpret_cast<long*>(lVptrAddr)+1,4); 這更符合數組的用法,因為lVptrAddr被轉成了long*型別,所以+1就是往后移sizeof(long)的長度

  三, 以一段代碼開始

  #include<iostream>

  using namespace std;

  class A{ //虛函數示例代碼2

  public:

  virtual void fun(){ cout<<"A::fun"<<endl;}

  virtual void fun2(){cout<<"A::fun2"<<endl;}

  };

  class B:public A{

  public:

  void fun(){ cout<<"B::fun"<<endl;}

  void fun2(){ cout<<"B::fun2"<<endl;}

  }; //end//虛函數示例代碼2

  int main(){

  void (A::*fun)(); //定義一個函數指針

  A *p=new B;

  fun=&A::fun;

  (p->*fun)();

  fun = &A::fun2;

  (p->*fun)();

  delete p;

  system("pause");

  }

  你能估算出輸出結果嗎?如果你估算出的結果是A::fun和A::fun2,呵呵,恭喜恭喜,你中圈套了。其實真正的結果是B::fun和B::fun2,如果你想不通就接著往下看。給個提示,&A::fun和&A::fun2是真正獲得了虛函數的地址嗎?

  首先我們回到第二部分,通過段實作代碼,得到一個“通用”的獲得虛函數地址的方法

  #include<iostream>

  using namespace std;

  //將上面“虛函數示例代碼2”添加在這里

  void CallVirtualFun(void* pThis,int index=0){

  void (*funptr)(void*);

  long lVptrAddr;

  memcpy(&lVptrAddr,pThis,4);

  memcpy(&funptr,reinterpret_cast<long*>(lVptrAddr)+index,4);

  funptr(pThis); //調用

  }

  int main(){

  A* p=new B;

  CallVirtualFun(p); //調用虛函數p->fun()

  CallVirtualFun(p,1);//調用虛函數p->fun2()

  system("pause");

  }

  現在我們擁有一個“通用”的CallVirtualFun方法。

  這個通用方法和第三部分開始處的代碼有何聯系呢?聯系很大。由于A::fun()和A::fun2()是虛函數,所以&A::fun和&A::fun2獲得的不是函數的地址,而是一段間接獲得虛函數地址的一段代碼的地址,我們形象地把這段代碼看作那段CallVirtualFun。編譯器在編譯時,會提供類似于CallVirtualFun這樣的代碼,當你調用虛函數時,其實就是先調用的那段類似CallVirtualFun的代碼,通過這段代碼,獲得虛函數地址后,最后調用虛函數,這樣就真正保證了多態(tài)性。同時大家都說虛函數的效率低,其原因就是,在調用虛函數之前,還調用了獲得虛函數地址的代碼。

  最后的說明:本文的代碼可以用VC6和Dev-C++4.9.8.0通過編譯,且運行無問題。其他的編譯器小弟不敢保證。其中,里面的類比方法只能看成模型,因為不同的編譯器的低層實現是不同的。例如this指針,Dev-C++的gcc就是通過壓棧,當作參數傳遞,而VC的編譯器則通過取出地址保存在ecx中。所以這些類比方法不能當作具體實現

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/hujiangnet/archive/2008/11/01/3201746.aspx

posted on 2011-05-06 13:02 厚積薄發(fā) 閱讀(410) 評論(0)  編輯 收藏 引用 所屬分類: C/C++

導航

<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

統(tǒng)計

常用鏈接

留言簿

隨筆分類

文章分類

文章檔案

搜索

最新評論

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲精品网站在线播放gif| 欧美电影免费观看网站| 亚洲另类在线视频| 性高湖久久久久久久久| 亚洲香蕉视频| 欧美激情aⅴ一区二区三区| 久久久久一区二区三区| 国产老女人精品毛片久久| 亚洲毛片一区| 中文国产亚洲喷潮| 欧美大片18| 亚洲缚视频在线观看| 国产亚洲欧美在线| 亚洲午夜伦理| 亚洲欧美日韩第一区| 欧美老女人xx| 亚洲精品在线视频| 亚洲伦伦在线| 欧美黄色成人网| 亚洲人成人一区二区在线观看| 在线精品一区| 久久尤物视频| 亚洲成人在线免费| 亚洲国产成人91精品| 在线不卡视频| 久久五月激情| 欧美国产一区二区在线观看 | 午夜精品福利在线观看| 欧美日韩三级在线| 日韩视频一区二区三区在线播放免费观看 | 欧美精品一区在线播放| 91久久夜色精品国产九色| 亚洲精品乱码久久久久久按摩观 | 国产精品女主播一区二区三区| 99这里只有久久精品视频| 亚洲视频精品在线| 国产精品久久久久久久久久免费| 亚洲午夜精品福利| 久久精品亚洲| 一区二区在线观看视频| 免费高清在线一区| 亚洲理伦电影| 欧美一区在线直播| 在线观看中文字幕不卡| 欧美成人免费小视频| 亚洲精品久久久久久久久久久| 亚洲视频第一页| 国产亚洲午夜高清国产拍精品| 久久久精品国产免大香伊| 欧美激情精品久久久久久变态| 亚洲最新视频在线| 国产精品亚洲视频| 久久性色av| 一区二区精品在线| 久久久久www| 一区电影在线观看| 国产嫩草一区二区三区在线观看| 久久久噜噜噜久久久| 亚洲人www| 久久久国产一区二区| 亚洲人成网在线播放| 国产精品久久久久久久久久ktv| 久久成人羞羞网站| 亚洲精品午夜| 久久综合精品国产一区二区三区| 日韩视频免费观看高清完整版| 国产女人aaa级久久久级| 女女同性精品视频| 性亚洲最疯狂xxxx高清| 亚洲国产美女精品久久久久∴| 性做久久久久久久免费看| 亚洲国产精品女人久久久| 欧美午夜片欧美片在线观看| 久久久免费av| 亚洲一区二区在线视频| 亚洲经典在线| 每日更新成人在线视频| 亚洲欧美综合国产精品一区| 亚洲黄色在线视频| 国产一区二区三区网站| 欧美午夜在线观看| 欧美国产精品人人做人人爱| 久久av在线看| 亚洲综合首页| 在线视频精品一区| 亚洲欧洲一区| 欧美成年人视频网站| 久久九九国产精品| 午夜欧美精品久久久久久久| 一区二区三区成人| 日韩特黄影片| 亚洲第一色在线| 国内精品久久久久影院薰衣草| 国产精品视频xxxx| 欧美日韩中文字幕在线视频| 欧美激情视频给我| 欧美高清免费| 免费91麻豆精品国产自产在线观看| 欧美一区视频| 亚洲免费一区二区| 亚洲免费在线视频| 亚洲综合日韩在线| 亚洲一区免费在线观看| 一区二区三区久久精品| 亚洲免费黄色| 一区二区精品| 亚洲天堂成人在线观看| 亚洲午夜视频在线| 亚洲宅男天堂在线观看无病毒| 宅男精品视频| 中文网丁香综合网| 午夜精品国产| 欧美伊人久久久久久午夜久久久久 | 国产乱人伦精品一区二区| 国产精品日日做人人爱| 国产精品亚洲成人| 国产一区二区三区在线观看免费| 国产一区二区精品久久91| 国内精品一区二区三区| 激情久久久久久久久久久久久久久久| 黄色精品一区二区| 亚洲国产一区二区在线| 日韩视频免费| 亚洲天堂成人| 久久精视频免费在线久久完整在线看 | 亚洲在线观看| 欧美一区二区三区在线视频| 久久久久国产精品麻豆ai换脸| 老牛影视一区二区三区| 欧美国产欧美亚洲国产日韩mv天天看完整| 免费观看在线综合| 亚洲欧洲日本国产| 亚洲一区免费在线观看| 久久久久这里只有精品| 欧美精品三级日韩久久| 国产伦精品免费视频| 在线精品一区二区| 亚洲图片你懂的| 久久嫩草精品久久久精品一| 欧美激情黄色片| 亚洲深夜福利网站| 久久频这里精品99香蕉| 欧美久久一级| 国内外成人免费激情在线视频网站| 亚洲国产一区二区三区青草影视| 亚洲午夜视频在线| 鲁大师成人一区二区三区| 亚洲精品视频免费| 久久久999精品视频| 欧美日韩免费一区二区三区视频| 国产日韩一区二区| 一区二区欧美国产| 六月丁香综合| 亚洲视频国产视频| 蜜桃av一区二区| 国产日韩欧美一二三区| 一区二区av在线| 欧美va天堂| 欧美一区二区三区视频免费播放 | 午夜精品亚洲一区二区三区嫩草| 蜜桃av噜噜一区二区三区| 一区二区高清视频在线观看| 麻豆精品在线观看| 国产亚洲成av人片在线观看桃 | 亚洲人成在线观看一区二区| 新片速递亚洲合集欧美合集| 亚洲欧洲精品一区二区三区波多野1战4 | 久久精品国产成人| 国产精品99久久久久久人 | 国产精品美女久久久久久2018 | 亚洲欧美精品suv| 亚洲国产精品久久久| 久久国产视频网站| 国产久一道中文一区| 亚洲深夜福利视频| 亚洲精品国产精品乱码不99按摩 | 久久国产精品久久久久久电车| 欧美三级第一页| 99精品国产热久久91蜜凸| 欧美激情二区三区| 裸体丰满少妇做受久久99精品| 国产一区二三区| 久久国产欧美日韩精品| 亚洲欧美日本视频在线观看| 欧美日韩中文字幕精品| av成人免费在线观看| 亚洲精品美女91| 欧美精品在线免费| 艳妇臀荡乳欲伦亚洲一区| 91久久夜色精品国产九色| 欧美大色视频| 99精品久久免费看蜜臀剧情介绍| 欧美激情国产日韩精品一区18| 久久一区二区精品| 亚洲国产日韩在线一区模特| 你懂的一区二区| 欧美va天堂| 一区二区三区精品视频| 一区二区三区不卡视频在线观看| 欧美午夜片欧美片在线观看|