程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 淺談C++中虛函數完成道理揭秘

淺談C++中虛函數完成道理揭秘

編輯:關於C++

淺談C++中虛函數完成道理揭秘。本站提示廣大學習愛好者:(淺談C++中虛函數完成道理揭秘)文章只能為提供參考,不一定能成為您想要的結果。以下是淺談C++中虛函數完成道理揭秘正文


編譯器究竟做了甚麼完成的虛函數的晚綁定呢?我們來探個畢竟。     

編譯器對每一個包括虛函數的類創立一個表(稱為V TA B L E)。在V TA B L E中,編譯器放置特定類的虛函數地址。在每一個帶有虛函數的類 中,編譯器機密地置一指針,稱為v p o i n t e r(縮寫為V P T R),指向這個對象的V TA B L E。經由過程基類指針做虛函數調 用時(也就是做多態挪用時),編譯器靜態地拔出獲得這個V P T R,並在V TA B L E表中查找函數地址的代碼,如許就可以挪用准確的函數使晚綁縛產生。為每一個類設置V TA B L E、初始化V P T R、為虛函數挪用拔出代碼,一切這些都是主動產生的,所以我們不用擔憂這些。應用虛函數, 這個對象的適合的函數就可以被挪用,哪怕在編譯器還不曉得這個對象的特定類型的情形下。(《C++編程思惟》)

在任何類中不存在顯示的類型信息,可對象中必需寄存類信息,不然類型弗成能在運轉時樹立。那這個類信息是甚麼呢?

我們來看上面幾個類:

class no_virtual

{
public:
  void fun1() const{}
  int fun2() const { return a; }
private:
  int a;
} class one_virtual
{
public:
  virtual void fun1() const{}
  int fun2() const { return a; }
private:
  int a;
} class two_virtual
{
public:
  virtual void fun1() const{}
  virtual int fun2() const { return a; }
private:
  int a;
} 

以上三個類中:

no_virtual沒有虛函數,sizeof(no_virtual)=4,

類no_virtual的長度就是其成員變量整型a的長度;

one_virtual有一個虛函數,sizeof(one_virtual)=8;

two_virtual 有兩個虛函數,sizeof(two_virtual)=8;

有一個虛函數和兩個虛函數的類的長度沒有差別,其實它們的長度就是no_virtual的 長度加一個void指針的長度,它反應出,假如有一個或多個虛函數,編譯器在這個構造中拔出一個指針( V P T R)。在one_virtual 和 two_virtual之間沒有差別。這是由於V P T R指向一個寄存地址的表,只須要一個指針,由於一切虛函數地址都包括在這個表中。 這個VPTR便可以看做類的類型信息。

那我們來看看編譯器是怎樣樹立VPTR指向的這個虛函數表的。先看上面兩個類:

class base
{
public:
  void bfun(){}
  virtual void vfun1(){}
  virtual int vfun2(){}
private:
  int a;
} class derived : public base
{
public:
  void dfun(){}
  virtual void vfun1(){}
  virtual int vfun3(){}
private:
  int b;
} 

兩個類VPTR指向的虛函數表(VTABLE)分離以下:

base類

 

      ——————
VPTR——> |&base::vfun1 |
      ——————
     |&base::vfun2 |
     ——————

derived類

      ———————
VPTR——> |&derived::vfun1 |
      ———————
     |&base::vfun2 |
     ———————
     |&derived::vfun3 |
     ———————

每當創立一個包括有虛函數的類或從包括有虛函數的類派生一個類時,編譯器就為這個類創立一個VTABLE,如上圖所示。在這個表中,編譯器放置了在這個類 中或在它的基類中一切已聲明為virtual的函數的地址。假如在這個派生類中沒有對在基類中聲明為virtual的函數停止從新界說,編譯器就應用基類 的這個虛函數地址。(在derived的VTABLE中,vfun2的進口就是這類情形。)然後編譯器在這個類中放置VPTR。當應用簡略繼續時,關於每 個對象只要一個VPTR。VPTR必需被初始化為指向響應的VTABLE,這在結構函數中產生。

一旦VPTR被初始化為指向響應的VTABLE,對象就"曉得"它本身是甚麼類型。但只要當虛函數被挪用時這類自我認知才有效。       

小我總結以下:

1、從包括虛函數的類派生一個類時,編譯器就為該類創立一個VTABLE。其每個表項是該類的虛函數地址。

2、在界說該派生類對象時,先挪用其基類的結構函數,然後再初始化VPTR,最初再挪用派生類的結構函數( 從二進制的視野來看,所謂基類子類是一個年夜構造體,個中this指針開首的四個字節寄存虛函數表頭指針。履行子類的結構函數的時刻,起首挪用基類結構函數,this指針作為參數,在基類結構函數中填入基類的vptr,然後回到子類的結構函數,填入子類的vptr,籠罩基類填入的vptr。如斯以來完成vptr的初始化。 )

3、在完成靜態綁准時,不克不及直接采取類對象,而必定要采取指針或許援用。由於采取類對象傳值方法,有暫時基類對象的發生,而采取指針,則是經由過程指針來拜訪內部的派生類對象的VPTR來到達拜訪派生類虛函數的成果。       

VPTR 經常位於對象的開首,編譯器能很輕易地取到VPTR的值,從而肯定VTABLE的地位。VPTR總指向VTABLE的開端地址,一切基類和它的子類的虛函 數地址(子類本身界說的虛函數除外)在VTABLE中存儲的地位老是雷同的,如下面base類和derived類的VTABLE中vfun1和vfun2 的地址老是按雷同的次序存儲。編譯器曉得vfun1位於VPTR處,vfun2位於VPTR+1處,是以在用基類指針挪用虛函數時,編譯器起首獲得指針指 向對象的類型信息(VPTR),然後就去挪用虛函數。如一個base類指針pBase指向了一個derived對象,那pBase->vfun2 ()被編譯器翻譯為 VPTR+1 的挪用,由於虛函數vfun2的地址在VTABLE中位於索引為1的地位上。同理,pBase->vfun3 ()被編譯器翻譯為 VPTR+2的挪用。這就是所謂的晚綁定。

以上這篇淺談C++中虛函數完成道理揭秘就是小編分享給年夜家的全體內容了,願望能給年夜家一個參考,也願望年夜家多多支撐。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved