內聯函數inline
- 概念:
以inline修飾的函數叫做內聯函數,編譯時C++編譯器會在調用內聯函數的地方展開,沒有函數壓棧的開銷, 從而能夠提升程序運行的效率。
- 特性:
- inline是一種以空間換時間的做法,省去調用函數的開銷。所以代碼很長或者有循環/遞歸的函數不適宜作爲內聯函數。
- inline對於編譯器而言只是一個建議,編譯器會自動優化,如果定義爲inline的函數體內有循環/遞歸等 ,編譯器優化時會忽略掉內聯。
- inline不建議聲明和定義分離,分離會導致鏈接錯誤。因爲inline被展開,就沒有函數地址了,鏈接就會找不到。
- 內聯函數不能是虛函數。(原因:inline是通過編譯器將函數內容替換到函數調用處,是靜態編譯的。而虛函數是動態調用的,在編譯器並不知道需要調用的是父類還是子類的虛函數,所以不能夠inline聲明展開,因此編譯器會忽略)
- 使用:
- 在C++中,在類的內部定義了函數體的成員函數默認是內聯函數,不管是否有inline關鍵字。
- 在內聯函數內不允許用循環語句、開關語句和遞歸調用等,且函數體不宜過長,否則作爲普通函數處理。
- 內聯函數的定義必須出現在第一次被調用之前。如果在前面聲明爲普通函數,而在調用代碼後面才定義爲一個inline函數,程序可以通過編譯,但該函數沒有實現inline。
- 如果一個inline函數會在多個源文件中被用到,那麼必須把它定義在頭文件中。解析:如果內聯函數fun()定義在某個編譯單元A中,那麼其他編譯單元中調用fun()的地方時,可以編譯通過(此時並沒有展開,結合第三條,此時雖然頭文件聲明瞭該inline函數,但此時調用時,還沒定義,所以作爲普通函數處理)。當鏈接時將無法解析該符號,出現鏈接錯誤。 因爲inline函數是作爲內部連接存在的,只能夠被本模塊訪問。
- 特點:
inline函數是函數,有類型,要做類型檢查,因此安全可靠,可以得到一定效率的提升,這個是以增加空間的消耗爲代價。
宏替換#define
- 概念:
定義預編譯時處理的宏,只是簡單的字符串替換,無類型檢查。
- 特性:
- 在宏擴展時,只對宏名做簡單的代換,不做任何計算,也不做任何語法檢查。
- 宏由編譯預處理程序處理。
- 宏定義可出現在程序的任何位置。
- 在宏定義中可以使用已定義的宏名。
- 使用:
- 不帶參數的宏定義格式:#define 標識符 字符或字符串
- 帶參數的宏定義格式: #define 宏名(參數表) 使用參數的字符或字符串
- 特點:
define無類型,不做檢查就直接替換,因此並不安全,會帶有副作用;宏不能調試,會使得代碼變得龐大。
區別
- 內聯函數在編譯時展開,宏在預編譯時由預處理器進行展開。
- 內聯函數會檢查參數類型,宏定義不檢查函數參數 ,所以內聯函數更安全。
- inline函數是函數,宏不是函數。
- 宏在定義時要小心處理宏參數(一般情況是把參數用括弧括起來),否則易出現二義性,而內聯定義不會出現。
- 編譯內聯函數可以嵌入到目標代碼,宏只是簡單文本替換。
基於上述區別中的第2點,爲了保證安全性在C++中儘量使用inline代替宏。