我們看下面的函數,函數體中只有一行語句:
double Average(double total, int number){
return total/number;
}
定義這么簡單的函數有必要嗎?實際上,它還是有一些優點的:第一,它使程序更可讀;第二,它使這段代碼可以重復使用。但是,它也有缺點:當它被頻繁地調用的時候,由於調用函數的開銷,會對應用程序的性能(時間+空間效率,這兒特指時間)有損失。例如,Average在一個循環語句中重復調用幾千次,會降低程序的執行效率。
那么,有辦法避免函數調用的開銷嗎?對於上面的函數,我么可以把它定義為內聯函數的形式:
inline double Average(double total, int number){
return total/number;
}
函數的引入可以減少程序的目標代碼,實現程序代碼的共享。
函數調用需要時間和空間開銷,調用函數實際上將程序執行流程轉移到被調函數中,被調函數的代碼執行完后,再返回到調用的地方。這種調用操作要求調用前保護好現場並記憶執行的地址,返回后恢復現場,並按原來保存的地址繼續執行。對於較長的函數這種開銷可以忽略不計,但對於一些函數體代碼很短,又被頻繁調用的函數,就不能忽視這種開銷。引入內聯函數正是為了解決這個問題,提高程序的運行效率。
在程序編譯時,編譯器將程序中出現的內聯函數的調用表達式用內聯函數的函數體來進行替換。由於在編譯時將內聯函數體中的代碼替代到程序中,因此會增加目標程序代碼量,進而增加空間開銷,而在時間開銷上不象函數調用時那么大,可見它是以目標代碼的增加為代價來換取時間的節省。
◆總結:inline函數是提高運行時間效率,但卻增加了空間開銷。
即inline函數目的是:為了提高函數的執行效率(速度)。
非內聯函數調用有棧內存創建和釋放的開銷
在C中可以用宏代碼提高執行效率,宏代碼不是函數但使用起來像函數,編譯器用復制宏代碼的方式取代函數調用,省去了參數壓棧、生成匯編語言的CALL調用、返回參數、執行return等過程,從而提高速度。
◆使用宏的缺點:(1)容易出錯(預處理器在復制宏代碼時常常產生意想不到的邊際效應)
例如:#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;
(2)不可調試
(3)無法操作類的私有數據成員
C++函數內聯機制既具備宏代碼的效率,又增加了安全性,且可自由操作類的數據成員。
關鍵字inline必須與函數定義體放在一起才能使函數真正內聯,僅把inline放在函數聲明的前面不起任何作用。因為inlin是一種用於實現的關鍵字,不是一種用於聲明的關鍵字。
許多書籍把內聯函數的聲明、定義體前都加了inline關鍵字,但聲明前不應該加(加不加不會影響函數功能),因為聲明與定義不可混為一談。
★聲明、定義和語句
聲明:就是在向系統介紹名字(一個名字是一塊內存塊的別名),只是告訴編譯器這個名字值的類型及宣告該名字的存在性,僅此而已。
定義:則是分配存儲空間,即具有了存儲類型。
語句:程序的基本組成部分,分可執行語句(定義是)和不可執行語句(聲明是)。
在正式編寫程序語句前定義的一些全局變量或局部變量,在C中為聲明,C++中為定義。
例如:int a;//在標C中為聲明,是不可執行語句;在C++中為定義,是可執行語句
extern int a;//為聲明,是不可執行語句 CWinApp curApp;//對象定義是可執行語句
◆使用內聯函數時應注意以下幾個問題:
(1) 在一個文件中定義的內聯函數不能在另一個文件中使用。它們通常放在頭文件中共享。
(2) 內聯函數應該簡潔,只有幾個語句,如果語句較多,不適合於定義為內聯函數。
(3) 內聯函數體中,不能有循環語句、if語句或switch語句,否則,函數定義時即使有inline關鍵字,編譯器也會把該函數作為非內聯函數處理。
(4) 內聯函數要在函數被調用之前聲明。
例如:
#include <iostream.h>
int increment(int i);
inline int increment(int i){
i++; return i;
}
void main(void){ ……
}
如果我們修改一下程序,將內聯函數的定義移到main()之后:
#include <iostream.h>
int increment(int i);
void main(void){ ……
}
內聯函數定義放在main()函數之后
inline int increment(int i){
i++; return i;
}
內聯函數在調用之后才定義,這段程序在編譯的時候編譯器不會直接把它替換到main中。也就是說實際上"increment(int i)"只是作為一個普通函數被調用,並不具有內聯函數的性質,無法提高運行效率