內聯函數是C++的重要函數之一。因此,首先讓我們了解為什麽使用內聯函數,以及內聯函數的目的是什麽?
當程序執行函數調用指令時,CPU將存儲該函數調用之後的指令的內存地址,將函數的參數複製到堆棧上,最後將控製權轉移到指定的函數。然後,CPU執行函數代碼,將函數返回值存儲在預定義的存儲位置/寄存器中,並將控製權返回給調用函數。如果函數的執行時間少於從調用者函數到被調用函數(被調用者)的切換時間,則這可能會成為開銷。對於大型函數和/或執行複雜任務的函數,與函數運行所花費的時間相比,函數調用的開銷通常是微不足道的。但是,對於常用的小型函數而言,進行函數調用所需的時間通常比實際執行函數代碼所需的時間要多得多。對於小函數,由於小函數的執行時間少於切換時間,因此會產生開銷。
C++提供了一個內聯函數,以減少函數調用的開銷。內聯函數是在調用時在行中擴展的函數。調用內聯函數時,將在內聯函數調用時插入或替換內聯函數的整個代碼。此替換由C++編譯器在編譯時執行。如果內聯函數很小,則可以提高效率。
定義函數內聯的語法為:
inline return-type function-name(parameters) { // function code }
請記住,內聯隻是對編譯器的請求,而不是命令。編譯器可以忽略內聯請求。在以下情況下,編譯器可能不會執行內聯:
1)如果一個函數包含一個循環。 (用於do-while)
2)如果函數包含靜態變量。
3)如果函數是遞歸的。
4)如果函數的返回類型不是void,並且函數主體中不存在return語句。
5)如果函數包含switch或goto語句。
內聯函數具有以下優點:
1)不會發生函數調用開銷。
2)調用函數時,還可以節省堆棧中push /pop變量的開銷。
3)它還節省了從函數返回調用的開銷。
4)內聯函數時,可以使編譯器對函數主體執行特定於上下文的優化。對於普通的函數調用,這種優化是不可能的。通過考慮調用上下文和被調用上下文的流程可以獲得其他優化。
5)內聯函數可能對於嵌入式係統很有用(如果很小),因為內聯函數所產生的代碼少於函數調用的前導和返回。
內聯函數的缺點:
1)內聯函數中添加的變量會占用其他寄存器,在in-lining函數之後,如果要使用寄存器的變量編號增加超過它們的數量,則可能會增加寄存器變量資源利用的開銷。這意味著當在函數調用點替換內聯函數主體時,該函數使用的變量總數也會被插入。因此,將用於變量的寄存器數量也將增加。因此,如果函數內聯後的變量數急劇增加,則肯定會導致寄存器利用率增加。
2)如果使用太多的內聯函數,則由於重複執行相同的代碼,因此二進製可執行文件的大小將很大。
3)過多的內聯也會降低指令高速緩存命中率,從而降低了從高速緩存到主存儲器的指令獲取速度。
4)如果有人更改了內聯函數中的代碼,則內聯函數可能會增加編譯時間開銷,因此必須重新編譯所有調用位置,因為編譯器將要求再次替換所有代碼以反映更改,否則它將繼續使用舊函數。
5)內聯函數對於許多嵌入式係統可能沒有用。因為在嵌入式係統中,代碼大小比速度更重要。
6)內聯函數可能會導致崩潰,因為內聯可能會增加二進製可執行文件的大小。內存溢出會導致計算機性能下降。
下麵的程序演示了如何使用內聯函數。
#include <iostream>
using namespace std;
inline int cube(int s)
{
return s*s*s;
}
int main()
{
cout << "The cube of 3 is:" << cube(3) << "\n";
return 0;
} //Output:The cube of 3 is:27
內聯函數和類:
也可以在類內部定義內聯函數。實際上,該類內部定義的所有函數都是隱式內聯的。因此,這裏也適用所有內聯函數的限製。如果您需要在類中顯式聲明內聯函數,則隻需在類內部聲明該函數,然後使用inline關鍵字在類外對其進行定義。
例如:
class S
{
public:
inline int square(int s) // redundant use of inline
{
// this function is automatically inline
// function body
}
};
上麵的樣式被認為是不好的編程樣式。最好的編程風格是隻在類內部編寫函數的原型,並將其指定為函數定義中的內聯。
例如:
class S
{
public:
int square(int s); // declare the function
};
inline int S::square(int s) // use inline prefix
{
}
下麵的程序演示了此概念:
#include <iostream>
using namespace std;
class operation
{
int a,b,add,sub,mul;
float div;
public:
void get();
void sum();
void difference();
void product();
void division();
};
inline void operation::get()
{
cout << "Enter first value:";
cin >> a;
cout << "Enter second value:";
cin >> b;
}
inline void operation::sum()
{
add = a+b;
cout << "Addition of two numbers:" << a+b << "\n";
}
inline void operation::difference()
{
sub = a-b;
cout << "Difference of two numbers:" << a-b << "\n";
}
inline void operation::product()
{
mul = a*b;
cout << "Product of two numbers:" << a*b << "\n";
}
inline void operation::division()
{
div=a/b;
cout<<"Division of two numbers:"<<a/b<<"\n" ;
}
int main()
{
cout << "Program using inline function\n";
operation s;
s.get();
s.sum();
s.difference();
s.product();
s.division();
return 0;
}
輸出:
Enter first value:45 Enter second value:15 Addition of two numbers:60 Difference of two numbers:30 Product of two numbers:675 Division of two numbers:3
宏有什麽問題?
熟悉C語言的讀者知道C語言使用宏。預處理程序直接在宏代碼內替換所有宏調用。建議始終使用內聯函數而不是宏。據C++的創建者Bjarne Stroustrup博士說,在C++中,宏幾乎從不需要,而且容易出錯。在C++中使用宏存在一些問題。宏無法訪問類的私人成員。宏看起來像函數調用,但實際上不是。
例:
#include <iostream>
using namespace std;
class S
{
int m;
public:
#define MAC(S::m) // error
};
C++編譯器檢查內聯函數的參數類型,並且正確執行了必要的轉換。預處理程序宏無法執行此操作。另一件事是,宏由預處理器管理,內聯函數由C++編譯器管理。
切記:確實,該類內部定義的所有函數都是隱式內聯的,並且C++編譯器將對這些函數執行內聯調用,但如果該函數是虛擬的,則C++編譯器將無法執行內聯。原因是對虛擬函數的調用是在運行時而不是在編譯時解決的。虛擬方式要等到運行時才進行,而內聯方式要等到編譯期間,如果編譯器不知道將調用哪個函數,它如何執行內聯?
要記住的另一件事是,僅當函數調用期間花費的時間比函數主體執行時間多時,才使函數內聯是有用的。內聯函數完全無效的示例:
inline void show()
{
cout << "value of S = " << S << endl;
}
上麵的函數執行起來相對要花很長時間。通常,執行輸入輸出(I /O)操作的函數不應定義為內聯函數,因為它會花費大量時間。從技術上講,show()函數的內聯值是有限的,因為I /O語句將花費的時間遠遠超過了函數調用的開銷。
如果函數沒有內聯擴展,則取決於使用的編譯器,編譯器可能會向您顯示警告。 Java和C#等編程語言不支持內聯函數。
但是在Java中,編譯器可以在調用小型final方法時執行內聯,因為final方法不能被子類覆蓋,並且對final方法的調用在編譯時就可以解決。在C#中,JIT編譯器還可以通過內聯小型函數調用來優化代碼(例如,在循環中替換小型函數的主體)。
最後要記住的是,內聯函數是C++的寶貴函數。適當地使用內聯函數可以提高性能,但是如果任意使用內聯函數,則它們無法提供更好的結果。換句話說,不要指望程序會有更好的性能。不要使每個函數都內聯。最好使內聯函數盡可能小。
參考文獻:
1)Effective C++ , Scott Meyers
2)http://www.parashift.com/c++-faq/inline-and-perf.html
3)http://www.cplusplus.com/forum/articles/20600/
4)Thinking in C++, Volume 1, Bruce Eckel。
5)C++ the complete reference, Herbert Schildt
相關用法
- C++ Inline和Macro的區別用法及代碼示例
- C++ Inline和Normal的區別用法及代碼示例
- C/C++ fill()、fill_n()用法及代碼示例
- C語言 strtok()、strtok_r()用法及代碼示例
- C/C++ strdup()、strndup()用法及代碼示例
注:本文由純淨天空篩選整理自GeeksforGeeks大神的英文原創作品 Inline Functions in C++。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。