1. 以內聯取代巨集程式碼
C++ 語言支援函數內聯,其目的是為了提高函數的執行效率(速度)。
在 C 程式中,可以用巨集程式碼提高執行效率。巨集程式碼本身不是函數,但使用起來象函 數。 預處理器以複製巨集程式碼的方式取代函數調用, 省去了參數壓棧、 產生組合語言的 CALL 調用、返回參數、執行 return 等過程,從而提高了速度。使用巨集程式碼最大的缺點是容 易出錯 ,預處理器在複製巨集程式碼時常常產生意想不到的邊際效應。
例如
#define MAX(a, b) (a) > (b) ? (a) : (b)
語句
result = MAX(i, j) + 2 ;
將被預處理器解釋為
result = (i) > (j) ? (i) : (j) + 2 ;
由於運算符' +'比運算符' :'的優先順序
result = ( (i) > (j) ? (i) : (j) ) + 2 ;
如果把巨集程式碼改寫為
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
則可以解決由優先權所造成的錯誤。但即使使用修改後的巨集程式碼也不是萬無一失的,例如語句
result = MAX(i++, j);
將被預處理器解釋為
result = (i++) > (j) ? (i++) : (j);
對於C++ 而言,使用巨集程式碼還有另一個缺點:無法操作類別的私人資料成員,也就是說宏程式碼基本上是針對公共或全域操作的。
讓我們看看 C++ 的「函數內聯」是如何運作的。對於任何內聯函數,編譯器在符號 表裡放入函數的宣告(包括名字、參數類型、傳回值型別) 。如果編譯器沒有發現內聯 函數有錯誤,那麼函數的程式碼也被放入符號表裡。在呼叫一個內聯函數時,編譯器 首先檢查呼叫是否正確(進行型別安全檢查,或進行自動型別轉換,當然對所有的函 數都一樣) 。如果正確,內聯函數的程式碼就會直接替換函數調用,於是省去了函數調用 的開銷。這個過程與預處理有顯著的不同,因為預處理器不能進行類型安全檢查 ,或 進行自動類型轉換。假如內嵌函數是成員函數,物件的位址( this)會被放在適當的地 方,這也是預處理器辦不到的。
C++ 語言的函數內聯機制既具備巨集程式碼的效率,也增加了安全性,而且可以自由操 作類的資料成員。所以在 C++ 程式中,應該用內聯函數取代所有巨集程式碼, “斷言 assert” 恐怕是唯一的例外。 assert 是僅在 Debug 版本起作用的宏,它用於檢查「不應該」發生 的情況。為了不在程式的 Debug 版本和 Release 版本引起差別, assert 不應該產生任何 副作用。 如果 assert 是函數, 由於函數呼叫會造成記憶體、 程式碼的變動, 那麼將導致 Debug 版本與 Release 版本有差異。 所以 assert 不是函數, 而是宏。
2. 內聯函數的程式設計風格
關鍵字 inline 必須與函數定義 體放在一起才能使函數成為內聯 ,僅將 inline 放在 函數聲明前面不起任何作用 作用。如下風格的函數Foo 不能成為內聯函數:
inline void Foo(int x, int y); // inline 仅与函数声明放在一起,不起任何作用 void Foo(int x, int y) { … }
而如下風格的函數 Foo 則成為內聯函數:
void Foo(int x, int y); inline void Foo(int x, int y) // inline 与函数定义体放在一起 { … }
所以說, inline 是一種「用於實現的關鍵字」 ,
所以說, inline 是一種「用於實現的關鍵字」 ,而不是一種「用於聲明的關鍵字」 。 一般地,使用者可以閱讀函數的聲明,但是看不到函數的定義。儘管在大多數教科書中內 聯函數的聲明、定義體前面都加了 inline 關鍵字,但我認為 inline 不應該出現在函數 的聲明中。這個細節雖然不會影響函數的功能,但是體現了高品質 C++/C 程式設計風格 的一個基本原則:聲明與定義不可混為一談,使用者沒有必要、也不應該知道函數是否需 要內聯。
定義在類別聲明之中的成員函數將自動地成為內聯函數,例如
class A { public: void Foo(int x, int y) { … } // 自动地成为内联函数 }將成員函數的定義體放在類別聲明之中雖然能帶來書寫上的方便,但不是一種好的程式風格,上例應該改成:
// 头文件 class A { public: void Foo(int x, int y); } // 定义文件 inline void A::Foo(int x, int y) { … }
內聯能提高函數的執行效率,為什麼不把所有的函數都定義成內聯函數?
如果所有的函數都是內聯函數,還用得著「內聯」這個關鍵字嗎?
內聯是以程式碼膨脹 (複製)為代價,僅僅省去了函數呼叫的開銷,從而提高函數的 執行效率。如果執行函數體內程式碼的時間,相較於函數呼叫的開銷較大,那麼效率的收 獲會很少。另一方面,每一個內聯函數的呼叫都要複製程式碼,將使程式的總程式碼量增大,
消耗更多的記憶體空間。以下情況不宜使用內聯:
( 1)如果函數體內的程式碼比較長,使用內聯將導致記憶體消耗代價較高。
( 2)如果函數體內出現循環,那麼執行函數體內程式碼的時間會比函數呼叫的開銷大。
類別的建構子和析構函式容易讓人誤解成使用內聯更有效。要當心建構函式和析構 函式可能會隱藏一些行為,如「偷偷地」執行了基底類別或成員物件的建構子和析構函式。 所以不要隨便地將建構函式和析構函式的定義體放在類別宣告中 。
以上就是C++ 函數內聯的內容,更多相關內容請關注PHP中文網(www.php.cn)!