內聯函數的概念
因爲函數在調用時有空間和時間的開銷,特別是多次重複調用時開銷很大。所以C++中就引入內聯函數的概念,適用於短小,功能簡單,頻繁調用的函數,比如swap函數。
內聯函數就是將函數調用處,用函數體替換,這樣就沒有函數壓棧的開銷,類似於宏替換。
簡單看一個例子,add函數:
/*code 1*/
#include<iostream>
using namespace std;
/*inline myadd(int a, int b){
return a + b;
}內聯函數的寫法*/
int myadd(int a, int b){
return a + b;
}
int main(){
int a = 5;
int b = 4;
int ret = myadd(a, b); //調用自定函數myadd
printf("%d\n", ret);
system("pause");
return 0;
}
上面的代碼在反彙編下可以看到myadd函數的調用情況:
給第五行前面加上inline,將myadd設成內聯函數,再查看彙編代碼如下:
這時沒有了函數調用壓棧的過程,這就是內聯函數。
內聯函數的本質
內聯函數在編譯過程中,會被編譯器將調用函數處替換成函數體,這樣就增長了程序的代碼,代碼區要複製多份內聯函數體的代碼,同時函數定義處的代碼會被消除。
所以內聯函數是典型的空間換時間的做法。
因爲內聯函數是程序員內部爲了優化程序的操作,用戶不需要,也不應該知道這個函數在內部編譯時是什麼過程,所以建議內聯函數只定義,不聲明。並且將定義寫在頭文件中(多文件編程),希望調用內聯函數時,引入對應的頭文件即可。
內聯函數的規範用法:
內聯函數的關鍵字inline,可以只在定義中添加,也可以只在聲明處添加,也可以兩個都添加。
但聲明和定義必須放在同一個文件,所以建議將內聯函數的定義放在頭文件中,那個文件中需要,就在那個文件中包入這個頭文件。
如果分開會有bug:
/*common.h*/
#include<iostream>
using namespace std;
inline void func();
/*func.h*/
#include"common.h"
//內聯函數的定義
void func(){
cout << "inline function" << endl;
}
/*main.cpp*/
#include"common.h"
int main(){
func();
return 0;
}
編譯時發生錯誤:
error LNK2019: 無法解析的外部符號 “void __cdecl func(void)” (?func@@YAXXZ),該符號在函數 _main 中被引用
內聯函數在編譯階段就會被替換。
func.cpp文件在編譯階段變成了:
#include<iostream>
using namespace std;
inline void func();
//內聯函數的定義
void func(){
cout << "inline function" << endl;
}
main.cpp在編譯階段變成了:
#include<iostream>
using namespace std;
inline void func();
int main(){
func();
return 0;
}
func.cpp 中有func()函數的定義和聲明,而main.cpp中只有函數的聲明和調用,沒有定義,這時編譯階段,程序是可以運行的,編譯器會在鏈接階段尋找main.cpp中對應的func()函數的定義,可是此時的內聯函數的定義已經被編譯器消除了,因爲它是內聯函數,而且沒有地址,所以main.cpp根本找不到這個函數。
在VS2013中,關於查看內聯函數的方法:
- 在release模式下。調試反彙編
- 在debug下,默認不優化,所以需要設置
- 屬性-》配置屬性-》C/C+±》常規-》調試信息格式-》程序數據庫(/Zi)
- C/C+±》優化-》內聯函數擴展-》只適用於————inline(/Obl)
總結:
內聯函數
- 消除函數調用時的開銷
- 取代帶參數的宏
- 缺點是:編譯後程序的體積會非常大,所以只將一些短小精煉、重複使用的函數作爲內聯函數。