c++從c中繼承的一個重要特征就是效率。假如c++的效率明顯低于c的效率,那么就會有很大的一批程序員不去使用c++了。
在c中我們經常把一些短并且執行頻繁的計算寫成宏,而不是函數,這樣做的理由是為了執行效率,宏可以避免函數調用的開銷,這些都由預處理來完成。
但是在c++出現之后,使用預處理宏會出現兩個問題:
1.第一個在c中也會出現,宏看起來像一個函數調用,但是會有隱藏一些難以發現的錯誤。
2.第二個問題是c++特有的,預處理器不允許訪問類的成員,也就是說預處理器宏不能用作類類的成員函數。
?
為了保持預處理宏的效率又增加安全性,而且還能像一般成員函數那樣可以在類里訪問自如,c++引入了內聯函數(inline function).
?內聯函數為了繼承宏函數的效率,沒有函數調用時開銷,然后又可以像普通函數那樣,可以進行參數,返回值類型的安全檢查,又可以作為成員函數。
?
預處理宏的缺陷
預處理器宏存在問題的關鍵是我們可能認為預處理器的行為和編譯器的行為是一樣的。當然也是由于宏函數調用和函數調用在外表看起來是一樣的,因為也容易被混淆。但是其中也會有一些微妙的問題出現:
問題一:
#define ADD(x,y) x+y inline int Add(int x,int y){return x + y; } void test(){int ret1 = ADD(10, 20) * 10; //希望的結果是300int ret2 = Add(10, 20) * 10; //希望結果也是300cout << "ret1:" << ret1 << endl; //210cout << "ret2:" << ret2 << endl; //300 }
問題二:
#define COMPARE(x,y) ((x) < (y) ? (x) : (y)) int Compare(int x,int y){return x < y ? x : y; } void test02(){int a = 1;int b = 3;//cout << "COMPARE(++a, b):" << COMPARE(++a, b) << endl; // 3cout << "Compare(int x,int y):" << Compare(++a, b) << endl; //2 }
問題三:
預定義宏函數沒有作用域概念,無法作為一個類的成員函數,也就是說預定義宏沒有辦法表示類的范圍。
?
內聯函數基本概念
在c++中,預定義宏的概念是用內聯函數來實現的,而內聯函數本身也是一個真正的函數。內聯函數具有普通函數的所有行為。唯一不同之處在于內聯函數會在適當的地方像預定義宏一樣展開,所以不需要函數調用的開銷。因此應該不使用宏,使用內聯函數
n?在普通函數(非成員函數)函數前面加上inline關鍵字使之成為內聯函數。但是必須注意必須函數體和聲明結合在一起,否則編譯器將它作為普通函數來對待。
inline?void?func(int?a); |
以上寫法沒有任何效果,僅僅是聲明函數,應該如下方式來做:
inline?int?func(int?a){return?++;} |
注意: 編譯器將會檢查函數參數列表使用是否正確,并返回值(進行必要的轉換)。這些事預處理器無法完成的。
內聯函數的確占用空間,但是內聯函數相對于普通函數的優勢只是省去了函數調用時候的壓棧,跳轉,返回的開銷。我們可以理解為內聯函數是以空間換時間。
?
類內部的內聯函數
為了定義內聯函數,通常必須在函數定義前面放一個inline關鍵字。但是在類內部定義內聯函數時并不是必須的。任何在類內部定義的函數自動成為內聯函數。
class Person{ public:Person(){ cout << "構造函數!" << endl; }void PrintPerson(){ cout << "輸出Person!" << endl; } }
?
構造函數Person,成員函數PrintPerson在類的內部定義,自動成為內聯函數。
?
內聯函數和編譯器
內聯函數并不是何時何地都有效,為了理解內聯函數何時有效,應該要知道編譯器碰到內聯函數會怎么處理?
對于任何類型的函數,編譯器會將函數類型(包括函數名字,參數類型,返回值類型)放入到符號表中。同樣,當編譯器看到內聯函數,并且對內聯函數體進行分析沒有發現錯誤時,也會將內聯函數放入符號表。
當調用一個內聯函數的時候,編譯器首先確保傳入參數類型是正確匹配的,或者如果類型不正完全匹配,但是可以將其轉換為正確類型,并且返回值在目標表達式里匹配正確類型,或者可以轉換為目標類型,內聯函數就會直接替換函數調用,這就消除了函數調用的開銷。假如內聯函數是成員函數,對象this指針也會被放入合適位置。
類型檢查和類型轉換、包括在合適位置放入對象this指針這些都是預處理器不能完成的。
?
但是c++內聯編譯會有一些限制,以下情況編譯器可能考慮不會將函數進行內聯編譯:
ü?不能存在任何形式的循環語句 ü?不能存在過多的條件判斷語句 ü?函數體不能過于龐大 ü?不能對函數進行取址操作 |
?
內聯僅僅只是給編譯器一個建議,編譯器不一定會接受這種建議,如果你沒有將函數聲明為內聯函數,那么編譯器也可能將此函數做內聯編譯。一個好的編譯器將會內聯小的、簡單的函數。