一、?
是指那些定義在類體內的成員函數,也就該函數的函數體放在類內。
二、爲什麼要使用inline函數
首先引入關於調用函數的缺點:函數調用前要先保存寄存器,並在返回時恢復;複製實參;程序還必須轉向一個新位置執行。
將一個函數聲明爲inline,那麼函數就成爲內聯函數。內聯函數通常就是它在程序中每個調用點上“內聯地”展開。從定義上看,內聯函數跟一般函數不一樣,一般函數調用的時候是需要調用開銷的(比如出棧入棧等操作),內聯函數從定義上看更像是宏,但是跟宏不一樣。
內聯函數的作用主要就是使用在一些短小而使用非常頻繁的函數中,爲了減少函數調用的開銷,爲了避免使用宏(在c++中,宏是不建議使用的)。比如內聯函數inline int func(int x){return x*x;} 在調用的時候cout<<func(x)<<endl,在編譯時將被展開爲:
cout<<(x*x)<<endl;
三、內聯函數相對於宏的區別和優點
從上面的分析中,可以看出,內聯函數在表現形式上與宏很類似。但是內聯函數和宏之間的區別很明顯。宏是在預處理時進行的機械替換,內聯是在編譯時進行的。內聯函數是真正的函數,只是在調用時,沒有調用開銷,像宏一樣進行展開。內聯函數會進行參數匹配檢查,相對於帶參數的宏有很好的優點,避免了處理宏的一些問題。
四、內聯函數相對於宏的區別和優點
要讓一個函數稱爲內聯函數,有兩種方法:一種是把函數加上inline關鍵字;一種是在類的說明部分定義的函數,默認就是內聯的。
要禁止編譯器進行內聯,可以使用#pragma auto_inline編譯指令或者改變編譯參數。
五、內聯函數的實現
下面引自林銳博士的《高質量C++編程》P67定義在類聲明之中的成員函數將自動地成爲內聯函數,例如
class A
{
public:
void Foo(int x,int y);{ } // 自動地成爲內聯函數
}
將成員函數的定義體放在類聲明之中雖然能帶來書寫上的方便,但不是一種良好的編程風格,上例應該改成:
// 頭文件:
class A
{
public:
void Foo(int x, int y);
}
// 定義頭文件
inline void A::Foo(int x, int y)
{
}
C++的發明者Bjarne Stroustrup博士在他所著的書《C++語言的設計和演化》中則是這樣論述這個問題(見《C++語言的設計和演化》P12):
在帶類的C中只有成員函數能做成在線(注:也就是設計爲inline函數)的,而要求函數成爲在線只有一種方式,那就是把它的放進類的聲明之中。例如:
class stack
{
/* … /*
char pop()
{
If(top<=min) error(“stack underflow”);
return *--top;
}
};
事實上,那時也看到這會使類的聲明顯得比較雜亂。另一方面,這看起來也是個好東西,因爲它不鼓勵在線函數的過度使用。關鍵字inline和允許在線成員函數的功能都是後來由C++提供的。例如,在C++中可以寫下面這樣的代碼:
class stack
{
/* … /*
char pop();
};
Inline char stack:: pop()
{
If(top<=min) error(“stack underflow”);
return *--top;
}
所以這就與下面的第(3)點相對應
六、內聯函數注意事項
(1) 內聯函數一定會內聯展開嗎?答案是否定的。對於內聯函數,程序只是提供了一個“內聯建議”,即建議編譯器把函數用內聯展開,但是真正是否內聯,是由編譯器決定的,對於函數體過大的函數,編譯器一般不會內聯,即使制定爲內聯函數。
(2) 在內聯函數內部,不允許用循環語句和開關語句(if或switch)。內聯函數內部有循環和開關,也不會出錯,但是編譯器會把它當做非內聯函數的。
(3) 關鍵字inline必須與函數定義體放在一起才能使函數真正內聯,僅把inline放在函數聲明的前面不起任何作用。因爲inline是一種用於實現的關鍵字,不是一種用於聲明的關鍵字。內聯函數的聲明是不需要加inline關鍵字的,內聯函數的定義是必須加inline的(除了類的定義部分的默認內聯函數),儘管很多書聲明定義都加了,要注意理解聲明和定義的區別。
但是問題來了:(內聯函數是放在.h還是.cpp中)
參考大神的說法:首先介紹一個概念,“編譯單元”,用不太嚴謹的方式定義,就是當你把一個源文件(.cpp .cxx等)做完預處理,也就是把包含的頭文件的內容全部放到這個文件裏來,所有宏都展開,等等,形成的一個邏輯上的實體——就是編譯單元一個編譯單元可以單獨編譯,但不能鏈接成一個可執行程序(除非程序只有這個編譯單元); 有了編譯單元的概念以後,你只要確保以下這個原則就可以了:如果在這個編譯單元裏使用了一個Inline函數,那麼我在這個編譯單元結束之前,
必須能夠“看到”這個編譯單元的完整定義(所有實現代碼) 另外要注意,在編譯單元之內,調用inline函數的代碼行之前,至少要放置
一個這個inline函數的聲明,當然有定義也可以
從這個原則出發,最簡單的使用Inline函數的方法就是在頭文件定義,
否則你要在每一個使用inline函數的編譯單元裏一一定義這個函數,
如果有n個編譯單元,你就要把inline函數的代碼重複書寫n次
參考C++primer:內聯函數應該在頭文件中定義;在頭文件中加入或修改內聯函數時,使用了該頭文件的所有源文件都必須重新編譯。
(4) 在一個文件中定義的內聯函數不能在另一個文件中使用。它們通常放在頭文件中共享。
(5) 內聯函數的定義必須在第一次調用之前。注意,這裏是定義之前,不僅僅是聲明之前。對於普通函數,可以在調用之前聲明,調用代碼之後具體定義(實現函數),但是內聯函數要實現內聯,必須先定義再調用,否則編譯器會把在定義之前調用的內聯函數當做普通函數進行調用。
(6) 說明:上面這些inline的注意事項,在編程時要自己注意,因爲上面的注意事項不遵守很多並不會引起編譯錯誤,只是會導致寫了inline的函數不是內聯函數,從而與預期的目的不一樣。所以很多沒法用程序實例說明到底編譯器是按照inline還是非inline調用的,或許分析彙編代碼能看出,但是水平有限,就不多分析了。