天天看點

C++中實際項目開發中的内聯函數

        引入内聯函數的目的是為了解決程式中函數調用的效率問題,程式在編譯器編譯的時候,編譯器将程式中出現的内聯函數的調用表達式用内聯函數的函數體進行替換,而對于其他的函數,都是在運作時候才被替代。其實就是用空間的代價去節省時間。

 優點:當函數體比較小的時候,内聯該函數可以令目标代碼更加高效。 對于存取函數以及其它函數體比較短,,性能關鍵的函數鼓勵使用内聯。 

 缺點:濫用内聯将導緻程式變慢。内聯可能使目标代碼量或增或減,,這取決于内聯函數的大小。内聯非常短小的存取函數通常會減少代碼大小,但内聯一個相當大的函數将戲劇性的增加代碼大小。如今的處理器由于更好的利用了指令緩存,小巧的代碼往往執行更快。 

         是以内聯函數一般都是1-5行的小函數,甚至更少。在實際的項目開發的過程中,通常禁止将最終不會被編譯器成功内聯的函數定義為内聯。因為一旦函數被定義為内聯函數,無論該函數最終是否會被成功的内聯展開,所有使用了該函數的源檔案經過編譯後都會有一份該函數的執行個體,接着再由連結器将這其中備援的資料整理删除,這個開銷是非常大的,會大大延長程式build的時間。

滿足以下任一條件的函數都禁止定義為内聯函數:

  1. 函數語句超過5行
  2. 含有循環語句
  3. 含有swith語句
  4. 不止一次出現if語句或條件運算符
  5. 特殊的函數:例如遞歸函數、多态中的虛函數等
        針對上述第五點中的虛函數:如果類中定義了一個内聯虛函數,那麼僅當編譯器在編譯階段确切知道是這個類的對象在調用這個内聯虛函數時,才會将該函數内聯展開(假設該函數的開銷允許它成功内聯)。除此以外,通過諸如指針、引用等方式調用該虛函數,編譯器在編譯階段時無法知道運作時實際執行的代碼是什麼,是以絕對不會将該函數内聯展開。但在實際的多态開發的環境中,基本都是通過指針、引用之類的方式來調用虛函數。是以,類中定義的内聯虛函數就犯了“最終不會被編譯器成功内聯的函數定義為内聯”的錯誤,是以最好不要将虛函數定義為内聯函數。

        inline是實作關鍵字,不是聲明關鍵字,用在函數聲明時是無效的。根據C++編譯器的特性,内聯函數是無法跨源檔案的,被多個源檔案使用的内聯函數隻能同時将聲明和定義寫在同一個頭檔案中。如果内聯函數的聲明和定義分離在不同的檔案中,則隻有在實作了内聯函數的源檔案中可以正常使用該内聯函數,其它源檔案中若調用了該内聯函數,在連結階段會報連結錯誤,或是由編譯器提供一個非内聯版本的函數供其它源檔案使用,不會真正的展開。 此外,在類内就已經定義完成的函數,即使沒有加inline關鍵字,編譯器也會将其隐式定義為内聯函數,編譯時同一般的内聯函數相同處理。

         在常見的C++編譯器中,如果編碼者沒有為某個類提供構造/析構函數,編譯器會預設以内聯的方式生成這些函數。為沒有手動實作構造/析構函數的類實作一個空的、非内聯的構造/析構函數,在一定程度上也會減少程式build的時間。

 _forceinline指令: 

        在VC++中可使用另一關鍵字_forceinline 代替inline 關鍵字。這個關鍵字将指令編譯器跳過一般的ROI 分析(Return On Investment –一種程式設計縮略語),将所對應的代碼強行内聯。有些時候編譯器會拒絕将一個函數内聯,使用這個關鍵字,使用者隻得到一個編譯警告,就可強行進行内聯。
        但是在實際開發的過程中,從編譯和代碼膨脹的角度來看,__forceinline是一個非常危險的指令,它修改了編譯器對内聯函數處理上的預設行為。無論某個函數在開銷上是否适合内聯,該指令都會指令編譯器對這個函數進行強制内聯,__forceinline将最終是否成功内聯的決定權從編譯器轉交到了使用者手上,且該指令不具備可移植性,是以最好不要使用__forceinline指令。

繼續閱讀