在C語言中,我們使用宏定義函數這種借助編譯器的優化技術來減少程序的執行時間,那麼在C++中有沒有相同的技術或者更好的實現方法呢?答案是有的,那就是內聯函數。內聯函數作為編譯器優化手段的一種技術,在降低運行時間上非常有用。
我們將從:
1.什麼是內聯函數
2.為什麼要使用內聯函數
3.內聯函數優缺點分析
4.何時使用內聯函數
這四個方面對內聯函數進行介紹。
什麼是內聯函數
內聯函數是C++的增強特性之一,用來降低程序的運行時間。當內聯函數收到編譯器的指示時,即可發生內聯:編譯器將使用函數的定義體來替代函數調用語句,這種替代行為發生在編譯階段而非程序運行階段。
值得注意的是,內聯函數僅僅是對編譯器的內聯建議,編譯器是否覺得采取你的建議取決於函數是否符合內聯的有利條件。如何函數體非常大,那麼編譯器將忽略函數的內聯聲明,而將內聯函數作為普通函數處理。
如何使函數內聯
定義函數時,在函數的最前面以關鍵字“inline”聲明函數,即可使函數稱為內聯聲明函數。
例如:
Class A
{
Public:
inline int add(int a, int b)
{
return (a + b);
};
}
Class A
{
Public:
int add(int a, int b);
};
inline int A::add(int a, int b)
{
return (a + b);
}
為什麼要使用內聯函數
有時候我們會寫一些功能專一的函數,這些函數的函數體不大,包含了很少的執行語句。例如在計算1~1000以內的素數時,我們經常會使用開方操作使運算范圍縮小,這時我們會寫一個函數:
int root(int n)
{
return (int)sqrt((float)n);
}
然後我們的求范圍內素數的函數可以這樣寫。
int prime(int n)
{
int i;
for (i = 2; i <= root(n); i++)
{
if (n%i == 0)
return 0;
return 1;
}
}
當然,把root函數放在循環中不是個不明智的選擇,但想象一下,在某個程序上下文內必須頻繁地調用某個類似root的函數,其調用函數的花銷會有多大:當遇到普通函數的調用指令時,程序會保存當前函數的執行現場,將函數中的局部變量以及函數地址壓入堆棧,然後再將即將調用的新函數加載到內存中,這要經歷復制參數值、跳轉到所調用函數的內存位置、執行函數代碼、存儲函數返回值等過程,當函數執行完後,再獲取之前正在調用的函數的地址,回去繼續執行那個函數,運行時間開銷簡直太多了。
C++內聯函數提供了替代函數調用的方案,通過inline聲明,編譯器首先在函數調用處使用函數體本身語句替換了函數調用語句,然後編譯替換後的代碼。因此,通過內聯函數,編譯器不需要跳轉到內存其他地址去執行函數調用,也不需要保留函數調用時的現場數據。
inline函數的優缺點分析
通過下面這些優缺點總結你大概會更理解為什麼要使用inline函數:
優點:
1.它通過避免函數調用所帶來的開銷來提高你程序的運行速度。
2.當函數調用發生時,它節省了變量彈棧、壓棧的開銷。
3.它避免了一個函數執行完返回原現場的開銷。
4.通過將函數聲明為內聯,你可以把函數定義放在頭文件內。
缺點:
1.因為代碼的擴展,內聯函數增大了可執行程序的體積。
2.C++內聯函數的展開是中編譯階段,這就意味著如果你的內聯函數發生了改動,那麼就需要重新編譯代碼。
3.當你把內聯函數放在頭文件中時,它將會使你的頭文件信息變多,不過頭文件的使用者不用在意這些。
4.有時候內聯函數並不受到青睐,比如在嵌入式系統中,嵌入式系統的存儲約束可能不允許體積很大的可執行程序。
什麼時候該使用內聯函數
當程序設計需要時,每個函數都可以聲明為inline。下面列舉一些有用的建議:
1.當對程序執行性能有要求時,那麼就使用內聯函數吧。
2.當你想宏定義一個函數時,那就果斷使用內聯函數吧。
3.在類內部定義的函數會默認聲明為inline函數,這有利於 類實現細節的隱藏。
關鍵點
1. 內聯聲明只是一種對編譯器的建議,編譯器是否采用內聯措施由編譯器自己來決定。甚至在匯編階段或鏈接階段,一些沒有inline聲明的函數編譯器也會將它內聯展開。
2.編譯器的內聯看起來就像是代碼的復制與粘貼,這與預處理宏是很不同的:宏是強制的內聯展開,可能將會污染所有的命名空間與代碼,將為程序的調試帶來困難。
3.所有中類中定義的函數都默認聲明為inline函數,所有我們不用顯示地去聲明inline。
4.虛函數不允許內聯。
5.雖然說模板函數放中頭文件中,但它們不一定是內聯的。(不是說定義在頭文件中的函數都是內聯函數)。