內聯函數

內聯函數是一種編譯機制,優點從代碼上是看不出來的,但是程序的執行效率上有差別,通常,編譯器對函數調用的處理是一種類似中斷的方式,即當執行到函數調用語句時,程序把當前所有的狀態信息比如CPU所有寄存器(其中一個很重要的就是指令指針寄存器)的值保存起來,然後放心大膽地轉去執行那個函數的代碼,執行完後再返回原來的地方,恢復原先保存過的狀態信息,於是也就可以接着原來被中斷的指令繼續往下執行。這樣,就很容易實現代碼的結構化,因爲可以把一些獨立的功能模塊寫成函數,函數內部的變量和外部的變量互不影響,而且函數執行完後就可以釋放這個函數內部變量的所使用的內存空間(這就是爲什麼函數退出後,其內部變量不再有效),對內存的使用也是很經濟的(否則,如果一個大的程序全部由一個函數組成,那麼所有的變量都得自始至終地佔用內存空間),當然,還有其他優點,比如可以實現遞歸,總之是好處多多。
可是,任何事情往往都有兩方面,這樣做雖然好處多多,但也是有代價的,那就是前面所說的,任何一次函數調用,程序都得進行保存和恢復狀態信息的動作,用數據結構的術語說就是進棧和退棧,當然,還有內存分配的過程,如果函數的代碼非常少,這種代價並不是可忽略的,比如說,你編寫一個類,裏面有個記錄狀態的成員變量:
Class MyClass
{
private:
int m_iState;
}
按照面向對象的思想,函數的屬性應儘量的私有化,但外部怎麼獲得這個屬性值呢?一般的方法就是加一個共有函數,這就實現的面向對象思想中所謂“通過公用接口操作對象的私有屬性”。於是就變成了:
Class MyClass
{
public:
int GetState();
private:
int m_iState;
}

int MyClass::GetState()
{
return m_iState;
}
這樣一來,面向對象思想倒是體現出來了,但你的CPU會恨你:“你丫一個鳥函數就返回一個整數卻讓老子進一次棧、彈一次棧”,內存也會埋怨:“兄弟也得跟着分配內存!”
但對你來說,也很委屈,怎麼辦,把所有的屬性都改成public?讓外部內碼直接訪問?況且,那樣也不解決所有問題,因爲有時候即使不是爲了面向對象,我們也需要把獨立的功能模塊做成函數,比如說產生隨機數的函數。我想
int iRand=rand();
總比:
int iRand=((int)(MULTIPLIER * Seed + INCREMENT)>>16)&0x7fff;
看起來舒服吧?(我這裏只是打個比方,VC的rand函數並不是內聯函數)
而內聯函數就是解決這個問題了,對於程序員,他還是把獨立功能寫成函數的形式,但只要聲明爲內聯,編譯器就不把它編譯成一次函數調用,而只是類似於把函數的代碼拷貝到被調用的地方,而且這完全是編譯器私下裏完成的,原來的訪問權限等問題絲毫不受影響。這不是兩全齊美了嗎:在保證代碼的面向對象性和結構化不受損失的條件下,程序的效率也沒有損失,比如上面那個類,就變成了:
Class MyClass
{
public:
inline int GetState();
private:
int m_iState;
}

int inline MyClass::GetState()
{
return m_iState;
}
有一點要注意,內聯函數要跟類的聲明寫在同一個文件中,否則編譯會出錯。按照VC管理源文件的風格來說,就是內聯函數最好寫在聲明類的.h文件中,而不是像一般函數那樣寫在實現類的.cpp文件中。
當然,內聯函數還有另外一種寫法,就是直接寫在類中,此時,不必使用“inline”關鍵字。
Class MyClass
{
public:
int GetState(){ return m_iState; }
private:
int m_iState;
}
最後,還要注意,內聯函數只是一種編譯機制,用上面兩種形式聲明的函數僅僅是建議編譯器進行內聯,而編譯器是否內聯不一定。正如前面所說,函數調用的開銷只是對小的函數不可忽略,對於重量級的函數還是可以忽略的,而且在絕大多數的場合,函數調用纔是人間正道,纔是解決問題的最佳。所以大多數編譯器並不把帶有循環、遞歸等或者代碼比較多的函數進行內聯編譯,有的甚至不允許聲明成內聯的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章