內聯函數的功能和預處理宏的功能相似,在介紹內聯函數之前,先介紹一下預處理宏。宏是簡單字符替換,最常見的用法:定義了一個代表某個值的全局符號、定義可調用帶參數的宏。作為一種約定,習慣上總是用大寫字母來定義宏,宏還可以替代字符常量。我們會經常定義一些宏,如:
#define ADD(a,b) a+b
那為什么需要使用宏呢?因為調用函數需要一定的時間和空間開銷。
執行到函數調用指令時,程序將在函數調用后立即存儲該指令的內存地址,並將函數參數復制到堆棧(為此保留的內存塊),跳到標記函數起點的內存單元,執行函數代碼(也許還需將返回值放入寄存器中),然后跳回到地址被保存的指令處(這與閱讀文章時停下來看腳注,並在閱讀完腳注后返回到以前閱讀的地方類似)。來回跳躍並記錄跳躍位置意味着以前使用函數時,需要一定的開銷。
而宏僅僅是在預處理的地方把代碼展開,不需要額外的時間空間方面的開銷。
宏也有很多的不盡人意的地方,所以c++中用內聯函數來代替宏。
(1)宏不能訪問對象的私有成員。類的私有成員只能通過類的成員函數或友元函數來訪問,
(2)宏不進行類型檢查。例如上面定義的ADD宏,要注意傳入實參的類型,如果你傳入的參數不是char, int, float, double,而是其他類型,可能就會出錯。
(3)宏的定義很容易產生二義性。
#define MULTI(x) (x*x)
我們用一個數字去調用它,MULTI(10),這樣看上去沒有什么錯誤,結果返回100,是正確的;但是如果我們用MULTI(10+10)去調用的話,我們期望的結果是400,而宏的調用結果是(10+10*10+10),結果是120,這顯然不是我們要得到的結果。為避免這種錯誤,可給宏的參數都加上括號。
#define MULTI(x) ((x)*(x))
這樣可以確保MULTI(10+10)不會出錯,但是若使用MULTI(a++)調用它,他們本意是希望得到(a+1)*(a+1)的結果,但是宏的展開結果是:(a++)*(a++),如果a的值是2,我們得到的結果是2*3=6。而我們期望的結果是3*3=9。
內聯函數的方法很簡單,只需在函數首行的左端加一個關鍵字inline即可 。
在編譯時將所調用函數的代碼直接嵌入到主調函數中,而不是將流程轉出去,這種嵌入到主調函數中的函數成為內聯函數。
#include <iostream> using namespace std; inline int max(int,int, int); //聲明函數,注意左端有inline int main( ) { int i=10,j=20,k=30,m; m=max(i,j,k); cout<<″max=″<<m<<endl; return 0; } inline int max(int a,int b,int c) //定義max為內置函數 { if(b>a) a=b; //求a,b,c中的最大者 if(c>a) a=c; return a; }
由於在定義函數時指定它為內置函數,因此編譯系統在遇到函數調用“max(i,j,k)”時,就用max函數體的代碼代替“max(i,j,k)”,同時將實參代替形參。
這樣,程序代碼“m=max(i,j,k);”就被置換成:
if (j>i) i=j; if (k>i) i=k; m=i;
內聯函數必須是和函數體在一起,才有效。如下風格的函數不能成為內聯函數:
inline void Fun(int x, int y); // inline僅與函數聲明放在一起 void Fun (int x, int y) {…}
而如下風格的函數Fun則成為內聯函數:
void Fun(int x, int y); inline void Fun(int x, int y) {…} // inline與函數定義體放在一起
因此,inline是一種“用於實現的關鍵字”,而不是一種“用於聲明的關鍵字”。
內聯函數和宏的區別在於,宏是由預處理器對宏進行替代,而內聯函數是通過編譯器控制來實現的。而且內聯函數是真正的函數,只是在需要用到的時候,內聯函數像宏一樣的展開,所以取消了函數的參數壓棧,減少了調用的開銷。你可以象調用函數一樣來調用內聯函數,而不必擔心會產生於處理宏的一些問題。內聯函數與帶參數的宏定義進行下比較,它們的代碼效率是一樣,但是內聯歡函數要優於宏定義,因為內聯函數遵循的類型和作用域規則,它與一般函數更相近,在一些編譯器中,一旦關聯上內聯擴展,將與一般函數一樣進行調用,比較方便。
另外,宏定義在使用時只是簡單的文本替換,並沒有做嚴格的參數檢查,也就不能享受C++編譯器嚴格類型檢查的好處,另外它的返回值也不能被強制轉換為可轉換的合適的類型,這樣,它的使用就存在着一系列的隱患和局限性。C++的inline的提出就是為了完全取代宏定義,因為inline函數取消了宏定義的缺點,又很好地繼承了宏定義的優點
內聯函數是以代碼膨脹(復制)為代價,這省去了函數調用的開銷,從而提高函數的執行效率。另一方面,每一處內聯函數的調用都要復制代碼,將使程序的總代碼量增大,消耗更多的內存空間。以下情況不宜使用內聯:
(1)如果函數體內的代碼比較長,使用內聯將導致內存消耗代價較高。
(2)內聯函數中不能包括復雜的控制語句,如循環語句和switch語句。如果函數體內出現循環,那么執行函數體內代碼的時間要比函數調用的開銷大。
同時要注意inline關鍵字只是表示一個請求,編譯器並不會一定將inline修飾的函數作為內聯,有的沒有被inline修飾的函數在一些編譯器中也可能被編譯為內聯。