天天看點

深度探索C++對象模型 【第四章2】

1:關于取位址的藝術~

  • 直接取一個非靜态成員變量的位址時,得到的是其在class中的真實offset再加1。當綁定到真實的對象上再取其位址時,得到的是其在記憶體中的真正位址。
  • 取一個非靜态成員函數的位址時,得到的是其在記憶體中真正的位址,但是要存取該函數,也需要對象的位址來配合。
  • 直接存取一個靜态成員變量的位址時,無論通過對象還是class,得到的都是其在記憶體中的真正位址。
  • 直接存取一個靜态成員函數的位址時,無論通過對象還是class,得到的都是其在記憶體中的真正位址。

2:使用一個指向成員函數的指針,如果是虛函數、多重繼承、虛拟繼承的情況,其成本會更高一點。

double (Point::*pmf)() = &Point::z;
//pmf,一個指向成員函數的指針,被設定初值為Point::z(),調用方式:(point.*pmf)();或者(P->*pmf)();
           

3:指向虛函數的指針,虛函數機制仍然能夠在使用指向“成員函數指針”的情況下運轉!因為對一個虛函數取位址,由于其位址在編譯期是未知的,所能知道的隻是虛函數在vbtl中的索引值。也就是說,對虛成員函數取位址,所能獲得的是其索引值。

4:由上述2/3可知,一個指向成員函數的指針需要既可以指向非虛函數也可以指向虛函數,但是指向虛函數是,得到的隻是其索引值,而非虛函數得到的是其真實的位址。是以編譯器必須定義pmf使他能夠

  • 持有兩種數值
  • 使得該數值能夠區分索引值和真實的位址

5:cfront的做法是先進行一個判斷,将指針轉化為int,進行一定的判斷,這種做法最多隻能支援有128個虛函數,雖然有一定的限制,但卻可行。

6:多重繼承下指向成員函數的指針

  • 有的編譯器設計了一種結構體,裡面的資料分别儲存vbtl中的索引(初始值為-1)和非虛函數的位址
  • 不同編譯器有自己不同的風格

7:函數指針的效率而言,多重繼承和虛拟繼承一樣即使在編譯器優化的條件下還會有一定的額外時間負擔

8:inline函數

  • 将class中的成員存取函數聲明為inline,我們可以保持直接存取member的高效率,亦保持了函數的封裝性。
  • 關鍵詞inline隻是一種請求,如果這項請求被接受,編譯器會用一個表達式來合理地将這個函數展開。
  • 将函數展開之後所帶來的執行成本會比一般的函數調用以及傳回機制多帶來的成本要低。
  • 一般編譯器會計算函數中“指派”、“函數調用次數”、“虛函數調用次數”等等,每種類型會有一個相應的權值,而inline函數的複雜度就會以這些權值的總和來決定

9:編譯器的inline分析

  • 分析函數的定義,決定是否能轉化為inline,如果不行,他會被轉化為一個static函數,并在被編譯子產品内産生對應的函數定義。
  • 真正的inline函數擴充操作是在調用時才會發生。
  • 有很多的編譯器(UNIX)認為不值得在inline上大費周章,是以想要看其是否真正實作了inline,你得去彙編器裡看看

10: 在inline函數的擴充期間

  • 每一個形式參數都會被轉化為實際參數,如果有常量表達式的出現,會在替換之前完成對其的求值操作,後繼的inline替換,可以直接綁定。

11:在inline函數中,每一個局部變量都必須被放在函數調用的一個封閉區段中,擁有一個獨一無二的名稱。這樣可能會導師大量的臨時性對象的産生。

12:inline函數對于封裝提供了一種必要的支援,可以有效存儲Nonpublic中的資料。但如果其調用次數太多,可能會産生大量的擴充碼,使得程式大小暴漲。inline中再有inline可能會使得連鎖複雜度太高無法擴充。

繼續閱讀