突然看到C++Primer中講到,對於vector的一個迴圈,調用語句:(範例程式碼i=v.begin()不是很規範,雖然不會出錯,客官請將就著看)
[cpp] view plain copy for (int i=v.begin() ; i<v.size() ; i++) { .... } 對於size()的調用,其實是內聯。想到以前貌似是看《高品質C/C++編程》時,提過,在迴圈時,可以採用變數儲存v.size()的值,以減少每個迴圈的調用開支。於是決定一搜,順便總結之。
1、inline的引出
考慮下列min()函數(例子來自C++Primer第三版303頁)
[cpp] view plain copy int min( int v1, int v2 ) { return( v1 < v2 << v1 : v2 ); } 為這樣的小操作定義一個函數的好處是:
a.如果一段程式碼封裝含min()的調用,那閱讀這樣的代碼並解釋其含義比讀一個條件操作符的執行個體,可讀性會強很多。
b.改變一個局部化的實現比更改一個應用中的300個出現要容易得多
c.語義是統一的,每個測試都能保證相同的方式實現
d.函數可以被重用,不必為其他的應用重寫代碼
不過,將min()寫成函數有一個嚴重的缺點:調用函數比直接計算條件操作符要慢很多。那怎麼能兼顧以上優點和效率呢。C++提供的解決方案為inline(內聯)函數
2、inline的原理:代碼替代
在程式編譯時間,編譯器將程式中出現的內嵌函式的調用運算式用內嵌函式的函數體來進行替代。
例如,如果一個函數被指定為inline 函數則它將在程式中每個調用點上被內聯地展開例如
[cpp] view plain copy int minVal2 = min( i, j ); 在編譯時間被展開為
[cpp] view plain copy int minVal2 = i < j << i : j; 則把min()寫成函數的額外執行開銷從而被消除了。
3、inline的使用
讓一個函數成為內嵌函式,隱式的為在類裡定義函數,顯式的則是在函數前加上inline關鍵字說明。
4、使用inline的一些注意事項
a.從inline的原理,我們可以看出,inline的原理,是用空間換取時間的做法,是以代碼膨脹(複製)為代價,僅僅省去了函數調用的開銷,從而提高函數的執行效率。如果執行函數體內代碼的時間,相比於函數調用的開銷較大,那麼效率的收穫會很少。所以,如果函數體代碼過長或者函數體重有迴圈語句,if語句或switch語句或遞迴時,不宜用內聯
b.關鍵字inline 必須與函數定義體放在一起才能使函數成為內聯,僅將inline 放在函式宣告前面不起任何作用。內嵌函式調用前必須聲明。《高品質C/C++編程》裡一個例子。 [cpp] view plain copy inline void Foo(int x, int y); // inline 僅與函式宣告放在一起 void Foo(int x, int y) { ... } 以上代碼不能成為內嵌函式,而以下則可以
[cpp] view plain copy void Foo(int x, int y); inline void Foo(int x, int y) // inline 與函數定義體放在一起 { ... } 所以說,inline 是一種“用於實現的關鍵字”,而不是一種“用於聲明的關鍵字”。對於以上例子,林銳還建議,只在定義前加上inline,而不是在聲明和定義前都加,因為這能體現高品質C++/C 程式設計風格的一個基本原則:聲明與定義不可混為一談。
c.inline對於編譯器來說只是一個建議,編譯器可以選擇忽略該建議。換句話說,哪怕真的寫成了inline,也沒有任何錯誤的情況下,編譯器會自動進行最佳化。所以當inline中出現了遞迴,迴圈,或過多代碼時,編譯器自動無視inline聲明,同樣作為普通函數調用。
總結下:
樓主覺得可以將內聯理解為C++中對於函數專有的宏,對於C的函數宏的一種改進。對於常量宏,C++提供const替代;而對於函數宏,C++提供的方案則是inline。在C中,大家都知道宏的優勢,編譯器通過複製宏代碼的方式,省去了參數壓棧,產生彙編的call調用,返回參數等操作,雖然存在一些安全隱患,但在效率上,還是很可取的。
不過函數宏還是有不少缺陷的,主要有以下:
a.在複製代碼時,容易出現一想不到的邊際效應,比如經典的 [cpp] view plain copy #define MAX(a, b) (a) > (b) ? (a) : (b) 在執行語句: [cpp] view plain copy result = MAX(i, j) + 2 ; 時,會被解釋為
[cpp] view plain