C++編程之內聯函數,需要看編譯器心情的函數,你知道嗎?

題記,你知道的編程語言中有哪些類型的函數呢?如果你學過C++的,你知道內聯函數嗎?這個優秀又隱蔽的存在,本文將帶你初步瞭解一下內聯函數。


系列文章

C++編程之命名空間、const常量
C++編程之引用的詳細總結
C++中引用的本質到底是什麼?
C++中類的構造函數和析構函數(一)
C++編程之運算符重載


常規函數

    在程序的運行中,函數調用時,程序會跳到另一個地址(函數地址)中,執行到函數調用指令時,程序將在函數調用後存儲該指令的內存地址,並將函數參數複製到堆棧中,跳到標記函數起點的內存單元,執行函數代碼(可能還需要將返回值放入寄存器中),然後跳回到地址被保存的指令處。這樣來回跳躍以及入棧等頻繁操作,會消耗系統內存,增加系統開銷。因此在C/C++中,一般將短小精悍的函數寫成宏函數。

宏函數

    一般建議將短小、頻繁使用的函數封裝成宏函數,調用時,直接宏展開,跑源碼,減少入棧出棧的系統開銷。

#define ADD(x,y) x+y

int main()
{
	int a = 100;
    int b = 200;
    int c = ADD(a+b);
    // c=300
}

    但是宏函數還是有很多缺陷,比如:

int ret = ADD(10 + 20) * 20;  
// 按照常識一般是600,但運行結果其實是410.因爲宏展開是這樣的 10+20*20  按照運算符優先級,計算出是410

    那如何避免如上問題呢?最簡單的辦法是在每個操作符之間加入括號。
    但是如下示例,比較兩個數的大小,我們使用宏函數表示,每個該加括號的地方都加入括號

#define MyCompare(a,b) (((a)<(b))?(a):(b))

// 調用

int a=10;
int b=20;
MyCompare(a,b); // 正常
MyCompare(++a,b); //應該是多少呢?結果令人喫驚,居然是12.原因是宏展開後(((++a)<(b)?(++a):(b))),即發生了2次++。

    同樣示例如下


#define Square(x)(x)*(x)

int main()
{
	int a = 10;
    int b = Square(++a); // 結果不是預期
}

    因此,總結以下宏函數的缺點:

  • 運算完整性無法保證,需要加小括號;
  • 即使加入括號修飾符,有些情況依然會出現和預期不一樣的結果
  • 宏函數不重視作用域

基於以上缺點,在C++中引入了一個新的函數,那就是內聯函數。

內聯函數

    內聯函數具有普通函數的所有特性,但是還具有在適當的時候內類似宏展開,不會有入棧出棧的系統開銷。

內聯函數定義如下

inline int MyCompare(int a,int b)
{
	return a>b?a:b;
}

注意點:

  • 函數的聲明和實現必須同時加inline關鍵字,否則會按照普通方式處理
// TestInline.h

inline int MyCompare(int a, int b);

// TestInline.cpp

inline int MyCompare(int a, int b)
{
	return a > b ? a : b;  
}
  • 成員函數的前面隱藏的加了inline。
  • 內聯函數實現了空間換時間,提高效率。

    說到這裏,很多人可能會想,既然這麼好,那我以後所有的函數都寫內聯函數,那我要遺憾的告訴你,這就錯了!!!

    內聯函數並不是一直都有效的,還要看編譯器的心情。以下情況編譯器可能不考慮:

  1. 不能有任何形式的循環
  2. 不能存在過多的條件判斷語句
  3. 函數體不能過於龐大
  4. 不能對函數進行取址操作

    內聯僅僅是給編譯器的一個建議,編譯器不一定會按照你的建議,如果你沒將函數聲明爲內聯函數,編譯器可能會給你設置爲inline,好的編譯器會自動將一些頻繁調用短小的函數自動設置爲內聯函數。

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