在 C 語言中,我們使用了宏函式,這是一種編譯器使用的最佳化技術,用於減少執行時間等。那麼問題來了,C++ 有什麼類似的技術,而且是以什麼更好的方式呢?行內函數應運而生,這是一種編譯器使用的最佳化技術,尤其用於減少執行時間。我們將涵蓋行內函數的“是什麼、為什麼、何時及如何”。
什麼是行內函數行內函數是 C++ 的一項增強功能,用於提高程式的執行時間。可以指示編譯器將函式設為內聯,以便編譯器可以在呼叫這些函式定義的地方替換它們。編譯器在編譯時替換行內函數的定義,而不是在執行時引用函式定義。
注意:這只是對編譯器的一個建議,要求將其設為內聯。如果函式很大(就可執行指令而言),編譯器可能會忽略“inline”請求,並將該函式視為普通函式。
如何使函式內聯要使任何函式成為行內函數,請在其定義前加上關鍵字“inline”。
示例 –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
Class A
{
Public:
inline int add(int a, int b)
{
return (a + b);
};
}
Class A
{
Public:
int add(int a, int b);
};
inline int A::add(int a, int b)
{
return (a + b);
}
|
為什麼使用 –在許多地方,我們會為小的任務/功能建立函式,這些函式包含簡單且數量少的執行指令。想象一下它們每次被呼叫時產生的呼叫開銷。
遇到普通函式呼叫指令時,程式會儲存函式呼叫語句後面指令的記憶體地址,將要呼叫的函式載入到記憶體中,複製引數值,跳轉到被呼叫函式的記憶體位置,執行函式程式碼,儲存函式的返回值,然後跳轉回執行被呼叫函式之前儲存的指令地址。這會產生很大的執行時開銷。
C++ 行內函數提供了一種替代方案。使用 inline 關鍵字後,編譯器會將函式呼叫語句替換為函式程式碼本身(稱為展開過程),然後編譯整個程式碼。因此,有了行內函數,編譯器就不必跳轉到另一個位置來執行函式,然後再跳回來,因為被呼叫函式的程式碼已提供給呼叫程式。
透過以下優點、缺點和效能分析,您將能夠理解“為什麼”使用 inline 關鍵字
優點 :-
1. 透過避免函式呼叫開銷來加速程式。
2. 在發生函式呼叫時,節省了在堆疊上壓入/彈出變數的開銷。
3. 節省了從函式返回呼叫的開銷。
4. 透過利用指令快取來提高區域性性。
5. 透過標記為內聯,可以將函式定義放在標頭檔案中(即可以包含在多個編譯單元中,而不會導致連結器報錯)
缺點:-
1. 由於程式碼展開,增加了可執行檔案的大小。
2. C++ 內聯在編譯時解析。這意味著如果您更改了行內函數中的程式碼,您將需要重新編譯所有使用它的程式碼,以確保它已更新。
3. 在標頭檔案中使用時,它會使您的標頭檔案變大,其中包含使用者不關心的資訊。
4. 如上所述,它會增加可執行檔案的大小,這可能會導致記憶體抖動。更多的頁面錯誤會降低程式的效能。
5. 有時可能不適用,例如在嵌入式系統中,由於記憶體限制,不希望有大的可執行檔案。
何時使用 -可以根據程式設計師的需求將函式設為內聯。下面是一些有用的建議:
1. 當需要效能時,使用行內函數。
2. 使用行內函數而不是宏。
3. 傾向於在類外部使用內聯關鍵字以及函式定義,以隱藏實現細節。
關鍵點 -1. 這只是一個建議,並非強制。編譯器可能會或可能不會內聯您標記為內聯的函式。它也可能決定在編譯或連結時內聯未標記為內聯的函式。
2. 行內函數的工作方式類似於由編譯器控制的複製/貼上,這與預處理器宏有很大不同:宏會被強制內聯,會汙染所有名稱空間和程式碼,並且不容易除錯。
3. 類內部宣告和定義的所有成員函式預設都是內聯的。因此,無需顯式定義。
4. 虛擬函式不應被內聯。不過,有時當編譯器能確定物件型別時(例如,物件是在同一個函式體內部宣告和構造的),即使是虛擬函式也會被內聯,因為編譯器確切地知道物件的型別。
5. 模板方法/函式並不總是被內聯(它們出現在標頭檔案中不會使它們自動內聯)。
6. 大多數編譯器會對遞迴函式進行內聯,但有些編譯器提供了 #pragmas -
Microsoft C++ 編譯器 - inline_recursion(on),並且可以使用 inline_depth 控制其限制。
在 GCC 中,您還可以透過命令列引數 --max-inline-insns-recursive 來傳遞此選項。