內(nèi)聯(lián)函數(shù)是一種編譯機制,優(yōu)點從代碼上是看不出來的,但是程序的執(zhí)行效率上有差別,通常,編譯器對函數(shù)調(diào)用的處理是一種類似中斷的方式,即當執(zhí)行到函數(shù)調(diào)用語句時,程序把當前所有的狀態(tài)信息比如CPU所有寄存器(其中一個很重要的就是指令指針寄存器)的值保存起來,然后放心大膽地轉(zhuǎn)去執(zhí)行那個函數(shù)的代碼,執(zhí)行完后再返回原來的地方,恢復原先保存過的狀態(tài)信息,于是也就可以接著原來被中斷的指令繼續(xù)往下執(zhí)行。這樣,就很容易實現(xiàn)代碼的結(jié)構(gòu)化,因為可以把一些獨立的功能模塊寫成函數(shù),函數(shù)內(nèi)部的變量和外部的變量互不影響,而且函數(shù)執(zhí)行完后就可以釋放這個函數(shù)內(nèi)部變量的所使用的內(nèi)存空間(這就是為什么函數(shù)退出后,其內(nèi)部變量不再有效),對內(nèi)存的使用也是很經(jīng)濟的(否則,如果一個大的程序全部由一個函數(shù)組成,那么所有的變量都得自始至終地占用內(nèi)存空間),當然,還有其他優(yōu)點,比如可以實現(xiàn)遞歸,總之是好處多多。
可是,任何事情往往都有兩方面,這樣做雖然好處多多,但也是有代價的,那就是前面所說的,任何一次函數(shù)調(diào)用,程序都得進行保存和恢復狀態(tài)信息的動作,用數(shù)據(jù)結(jié)構(gòu)的術語說就是進棧和退棧,當然,還有內(nèi)存分配的過程,如果函數(shù)的代碼非常少,這種代價并不是可忽略的,比如說,你編寫一個類,里面有個記錄狀態(tài)的成員變量:
Class MyClass
{
private:
int m_iState;
}
按照面向?qū)ο蟮乃枷耄瘮?shù)的屬性應盡量的私有化,但外部怎么獲得這個屬性值呢?一般的方法就是加一個共有函數(shù),這就實現(xiàn)的面向?qū)ο笏枷胫兴^“通過公用接口操作對象的私有屬性”。于是就變成了:
Class MyClass
{
public:
int GetState();
private:
int m_iState;
}
int MyClass::GetState()
{
return m_iState;
}
這樣一來,面向?qū)ο笏枷氲故求w現(xiàn)出來了,但你的CPU會恨你:“你丫一個鳥函數(shù)就返回一個整數(shù)卻讓老子進一次棧、彈一次棧”,內(nèi)存也會埋怨:“兄弟也得跟著分配內(nèi)存!”
但對你來說,也很委屈,怎么辦,把所有的屬性都改成public?讓外部內(nèi)碼直接訪問?況且,那樣也不解決所有問題,因為有時候即使不是為了面向?qū)ο?,我們也需要把獨立的功能模塊做成函數(shù),比如說產(chǎn)生隨機數(shù)的函數(shù)。我想
int iRand=rand();
總比:
int iRand=((int)(MULTIPLIER * Seed + INCREMENT)>>16)&0x7fff;
看起來舒服吧?(我這里只是打個比方,VC的rand函數(shù)并不是內(nèi)聯(lián)函數(shù))
而內(nèi)聯(lián)函數(shù)就是解決這個問題了,對于程序員,他還是把獨立功能寫成函數(shù)的形式,但只要聲明為內(nèi)聯(lián),編譯器就不把它編譯成一次函數(shù)調(diào)用,而只是類似于把函數(shù)的代碼拷貝到被調(diào)用的地方,而且這完全是編譯器私下里完成的,原來的訪問權(quán)限等問題絲毫不受影響。這不是兩全齊美了嗎:在保證代碼的面向?qū)ο笮院徒Y(jié)構(gòu)化不受損失的條件下,程序的效率也沒有損失,比如上面那個類,就變成了:
Class MyClass
{
public:
inline int GetState();
private:
int m_iState;
}
int inline MyClass::GetState()
{
return m_iState;
}
有一點要注意,內(nèi)聯(lián)函數(shù)要跟類的聲明寫在同一個文件中,否則編譯會出錯。按照VC管理源文件的風格來說,就是內(nèi)聯(lián)函數(shù)最好寫在聲明類的.h文件中,而不是像一般函數(shù)那樣寫在實現(xiàn)類的.cpp文件中。
當然,內(nèi)聯(lián)函數(shù)還有另外一種寫法,就是直接寫在類中,此時,不必使用“inline”關鍵字。
Class MyClass
{
public:
int GetState(){ return m_iState; }
private:
int m_iState;
}
最后,還要注意,內(nèi)聯(lián)函數(shù)只是一種編譯機制,用上面兩種形式聲明的函數(shù)僅僅是建議編譯器進行內(nèi)聯(lián),而編譯器是否內(nèi)聯(lián)不一定。正如前面所說,函數(shù)調(diào)用的開銷只是對小的函數(shù)不可忽略,對于重量級的函數(shù)還是可以忽略的,而且在絕大多數(shù)的場合,函數(shù)調(diào)用才是人間正道,才是解決問題的最佳。所以大多數(shù)編譯器并不把帶有循環(huán)、遞歸等或者代碼比較多的函數(shù)進行內(nèi)聯(lián)編譯,有的甚至不允許聲明成內(nèi)聯(lián)的。
在解決C + +中宏存取私有的類成員的問題過程中,所有和預處理器宏有關的問題也隨著消失了。這是通過使宏被編譯器控制來實現(xiàn)的。在C + +中,宏的概念是作為內(nèi)聯(lián)函數(shù)來實現(xiàn)的,而內(nèi)聯(lián)函數(shù)無論在任何意義上都是真正的函數(shù)。唯一不同之處是內(nèi)聯(lián)函數(shù)在適當時像宏一樣展開,所以函數(shù)調(diào)用的開銷被取消。因此,應該永遠不使用宏,只使用內(nèi)聯(lián)函數(shù)。
任何在類中定義的函數(shù)自動地成為內(nèi)聯(lián)函數(shù),但也可以使用i n l i n e關鍵字放在類外定義的函數(shù)前面使之成為內(nèi)聯(lián)函數(shù)。但為了使之有效,必須使函數(shù)體和聲明結(jié)合在一起,否則,編譯器將它作為普通函數(shù)對待。因此
inline int PlusOne(int x);
沒有任何效果,僅僅只是聲明函數(shù)(這不一定能夠在稍后某個時候得到一個內(nèi)聯(lián)定義)。成功的方法如下:
inline int PlusOne(int x) { return ++x ;}
注意,編譯器將檢查函數(shù)參數(shù)列表使用是否正確,并返回值(進行必要的轉(zhuǎn)換)。這些事情是預處理器無法完成的。假如對于上面的內(nèi)聯(lián)函數(shù),我們寫成一個預處理器宏的話,將有不想要的副作用。
一般應該把內(nèi)聯(lián)定義放在頭文件里。當編譯器看到這個定義時,它把函數(shù)類型(函數(shù)名+返回值)和函數(shù)體放到符號表里。當使用函數(shù)時,編譯器檢查以確保調(diào)用是正確的且返回值被正確使用,然后將函數(shù)調(diào)用替換為函數(shù)體,因而消除了開銷。內(nèi)聯(lián)代碼的確占用空間,但假如函數(shù)較小,這實際上比為了一個普通函數(shù)調(diào)用而產(chǎn)生的代碼(參數(shù)壓棧和執(zhí)行C A L L)占用的空間還少。在頭文件里,內(nèi)聯(lián)函數(shù)默認為內(nèi)部連接——即它是static, 并且只能在它被包含的編譯單元看到。因而,只要它們不在相同的編譯單元中聲明,在內(nèi)聯(lián)函數(shù)和全局函數(shù)之間用同樣的名字也不會在連接時產(chǎn)生沖突。
為了定義內(nèi)聯(lián)函數(shù),通常必須在函數(shù)定義前面放一個i n l i n e關鍵字。但這在類內(nèi)部定義內(nèi)聯(lián)函數(shù)時并不是必須的。任何在類內(nèi)部定義的函數(shù)自動地為內(nèi)聯(lián)函數(shù)。如下例:
#include <iostream.h>
class point{
private:
int i,j,k;
public:
point() {i=j=k=0; }
point(int I,int J,int K) {
i=I;
j=J
k=K;
}
void print(const char* msg="") const{
if(*msg) cout<<"msg"<<endl;
cout<<"i="<<i<<endl;
cout<<"j="<<j<<endl;
cout<<"k="<<k<<endl;
}
};
main(){
point p,q(1,2,3);
p.print("value of p");
q.print("value of q");
}
當然,因為類內(nèi)部的內(nèi)聯(lián)函數(shù)節(jié)省了在外部定義成員函數(shù)的額外步驟,所以我們一定想在類聲明內(nèi)每一處都使用內(nèi)聯(lián)函數(shù)。但應記住,內(nèi)聯(lián)的目的是減少函數(shù)調(diào)用的開銷。假如函數(shù)較大,那么花費在函數(shù)體內(nèi)的時間相對于進出函數(shù)的時間的比例就會較大,所以收獲會較小。而且內(nèi)聯(lián)一個大函數(shù)將會使該函數(shù)所有被調(diào)用的地方都做代碼復制,結(jié)果代碼膨脹而在速度方面獲得的好處卻很少或者沒有。