【C++ 筆記】內嵌函數(inline function) - part 20

很感謝你點進來這篇文章。

你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!

內嵌函數(inline function)

內嵌函數或稱內聯函數。

在 C++,內嵌函數(inline function)是一種特殊的函數。

此函數需要用關鍵字 inline 於欲使用之函數名前方,使 compiler 將函數呼叫替換為函數的本體代碼,而非如普通函數要透過函數呼叫機制(如:跳轉到函數地址並回傳)。

這種方式可減少函數呼叫的開銷(如:stack 的 push、pop 操作),從而提高程式執行效率。

:::info
以下擷取至菜鳥教程的原理解釋:

C++ 內聯函數通常與類別一起使用。如果一個函數是內聯的,那麼在編譯時,編譯器會把函數的程式碼副本放置在每個呼叫該函數的地方。

對內聯函數進行任何修改,都需要重新編譯函數的所有客戶端,因為編譯器需要重新更換一次所有的程式碼,否則將繼續使用舊的函數。

https://www.runoob.com/cplusplus/cpp-inline-functions.html
:::

內嵌函數主要功能:優化小型、頻繁呼叫的函數。

以下是內嵌函數的語法:

1
inline return_type function_name(params)...

inline:關鍵字。

return_type:回傳型態。

function_name:函數名稱。

params:一個或以上的參數。

範例 1


1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
using namespace std;

inline int add(int a, int b) { // 使用關鍵字 inline
return a + b;
}

int main() {
int x = 5, y = 10;
cout << "Sum: " << add(x, y) << endl; // 編譯器可能將此替換為直接計算
return 0;
}

輸出結果:

1
Sum: 15

搭配範例 1:細講內嵌函數的原理


首先我們必須釐清,當一個普通函數被呼叫時,會有怎樣的行為:

  • 保存當前執行狀態(如:register)。
  • 將參數 push to stack 中。
  • 跳轉到函數的代碼位置。
  • 執行函數。
  • 回傳結果並恢復呼叫點的狀態。若為小型函數,呼叫的開銷可能比函數本身的執行時間大。

以下是我們用內嵌函數的運作原理(以範例 1 而言):

首先,編譯器會試著在呼叫點直接插入函數的代碼:

1
int result = add(5, 10);

然後會被編譯器直接換成:

1
int result = 5 + 10;

具體圖示可如下所示(Image Source:https://www.geeksforgeeks.org/inline-functions-cpp/):

image

內嵌函數的例外情況

Inlining is only a request to the compiler, not a command. The compiler may not perform inlining in such circumstances as:

Source:https://www.geeksforgeeks.org/inline-functions-cpp/

翻譯蒟蒻:inline 僅為對編譯器的「請求」,而不是一個指令。編譯器也許不會在以下這些情形執行 inline。

  • 函數內部含有迴圈
  • 函數內部含有 static 變數
  • 遞迴函數
  • 若函數回傳型態不是 void,並且函數體中不存在回傳語句。
  • 函數內部含有 switchgoto 語法

:::info
Tip:當函數只有 10 行甚至更少時才將其定義為內嵌函數。
:::

內嵌函數的優缺點

優點(Advantages):

  • 函數的呼叫開銷不會發生。
  • 可對函數主體進行優化。(具體事項如上的原理解釋)
  • 內嵌函數對於嵌入式系統(embedded systems:如 Arduino)可能是有用的(如果函數本體很小),因為 inline 可回傳更少的代碼。

缺點(Disadvantages):

  • 用於變數的 register 數量可能會增加。
  • 內嵌函數可能會導致 thrashing,inline 可能會增加二進位可執行檔的大小。
  • 若有人改了內嵌函數的代碼,則所有呼叫位置都必須重新編譯,因為編譯器需要再次替換所有代碼。
  • 內嵌函數對許多嵌入式系統可能不實用。因為在嵌入式系統中,程式碼大小比速度更重要(身為一個長期有在接觸 Arduino 的作者,我覺得這太他媽正確了)。

thrashing 可參見:https://en.wikipedia.org/wiki/Thrashing_(computer_science)

Source:https://www.geeksforgeeks.org/inline-functions-cpp/

範例 2:內嵌函數搭配 Class 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
using namespace std;

class Rectangle {
private:
int width, height;
public:
Rectangle(int w, int h) : width(w), height(h) {}

// 內嵌成員函數
inline int area() const {
return width * height;
}

// 外部定義的內嵌函數
inline int perimeter() const;
};

inline int Rectangle::perimeter() const {
return 2 * (width + height);
}

int main() {
Rectangle rect(5, 3);
cout << "Area: " << rect.area() << endl;
cout << "Perimeter: " << rect.perimeter() << endl;
return 0;
}

輸出結果:

1
2
Area: 15
Perimeter: 16

參考資料

Inline Functions in C++ - GeeksforGeeks

[C++]內嵌函數(inline function)筆記 | 郭董<3小花園 - 點部落

內嵌函式 (C++) | Microsoft Learn

C++ 内联函数 | 菜鸟教程