1. 程式人生 > >c++ 行內函數

c++ 行內函數

1.  行內函數

在C++中我們通常定義以下函式來求兩個整數的最大值:

複製程式碼 程式碼如下:
int max(int a, int b)
{
 return a > b ? a : b;
}

為這麼一個小的操作定義一個函式的好處有:

① 閱讀和理解函式 max 的呼叫,要比讀一條等價的條件表示式並解釋它的含義要容易得多

② 如果需要做任何修改,修改函式要比找出並修改每一處等價表示式容易得多

③ 使用函式可以確保統一的行為,每個測試都保證以相同的方式實現

④ 函式可以重用,不必為其他應用程式重寫程式碼

雖然有這麼多好處,但是寫成函式有一個潛在的缺點:呼叫函式比求解等價表示式要慢得多。在大多數的機器上,呼叫函式都要做很多工作:呼叫前要先儲存暫存器,並在返回時恢復,複製實參,程式還必須轉向一個新位置執行

C++中支援行內函數,其目的是為了提高函式的執行效率,用關鍵字 inline 放在函式定義(注意是定義而非宣告,下文繼續講到)的前面即可將函式指定為行內函數,行內函數通常就是將它在程式中的每個呼叫點上“內聯地”展開,假設我們將 max 定義為行內函數:

複製程式碼 程式碼如下:
inline int max(int a, int b)
{
 return a > b ? a : b;
}

則呼叫: cout<<max(a, b)<<endl;


在編譯時展開為: cout<<(a > b ? a : b)<<endl;

從而消除了把 max寫成函式的額外執行開銷

2.  行內函數和巨集

無論是《Effective C++》中的 “Prefer consts,enums,and inlines to #defines” 條款,還是《高質量程式設計指南——C++/C語言》中的“用函式內聯取代巨集”,巨集在C++中基本是被廢了,在書《高質量程式設計指南——C++/C語言》中這樣解釋到:

3.  將行內函數放入標頭檔案

關鍵字 inline 必須與函式定義體放在一起才能使函式成為內聯,僅將 inline 放在函式宣告前面不起任何作用。

如下風格的函式 Foo 不能成為行內函數:

複製程式碼 程式碼如下:
inline void Foo(int x, int y);   // inline 僅與函式宣告放在一起  
void Foo(int x, int y)
{
 …
}

而如下風格的函式 Foo 則成為行內函數:

複製程式碼 程式碼如下:
void Foo(int x, int y);  
inline void Foo(int x, int y)   // inline 與函式定義體放在一起
{
 …
}

所以說,C++ inline函式是一種“用於實現的關鍵字”,而不是一種“用於宣告的關鍵字”。一般地,使用者可以閱讀函式的宣告,但是看不到函式的定義。儘管在大多數教科書中行內函數的宣告、定義體前面都加了 inline 關鍵字,但我認為 inline 不應該出現在函式的宣告中。這個細節雖然不會影響函式的功能,但是體現了高質量C++/C 程式設計風格的一個基本原則:宣告與定義不可混為一談,使用者沒有必要、也不應該知道函式是否需要內聯。

定義在類宣告之中的成員函式將自動地成為行內函數,例如:

複製程式碼 程式碼如下:
class A

public:
 void Foo(int x, int y) { … }   // 自動地成為行內函數 
}

但是編譯器是否將它真正內聯則要看 Foo函式如何定義

行內函數應該在標頭檔案中定義,這一點不同於其他函式。編譯器在呼叫點內聯展開函式的程式碼時,必須能夠找到 inline 函式的定義才能將呼叫函式替換為函式程式碼,而對於在標頭檔案中僅有函式宣告是不夠的。

當然行內函數定義也可以放在原始檔中,但此時只有定義的那個原始檔可以用它,而且必須為每個原始檔拷貝一份定義(即每個原始檔裡的定義必須是完全相同的),當然即使是放在標頭檔案中,也是對每個定義做一份拷貝,只不過是編譯器替你完成這種拷貝罷了。但相比於放在原始檔中,放在標頭檔案中既能夠確保呼叫函式是定義是相同的,又能夠保證在呼叫點能夠找到函式定義從而完成內聯(替換)。

但是你會很奇怪,重複定義那麼多次,不會產生連結錯誤?

我們來看一個例子:

A.h :

複製程式碼 程式碼如下:
class A
{
public:
 A(int a, int b) : a(a),b(b){}
 int max();

private:
 int a;
 int b;
};

A.cpp :

複製程式碼 程式碼如下: #include “A.h”

inline int A::max()
{
 return a > b ? a : b;
}

Main.cpp :

複製程式碼 程式碼如下: #include <iostream>
#include “A.h”
using namespace std;

inline int A::max()
{
 return a > b ? a : b;
}

int main()
{
 A a(3, 5);
 cout<<a.max()<<endl;
 return 0;
}

一切正常編譯,輸出結果:5


倘若你在Main.cpp中沒有定義max行內函數,那麼會出現連結錯誤:

error LNK2001: unresolved external symbol “public: int __thiscall A::max(void)” ([email protected]@@QAEHXZ)main.obj
找不到函式的定義,所以行內函數可以在程式中定義不止一次,只要 inline 函式的定義在某個原始檔中只出現一次,而且在所有原始檔中,其定義必須是完全相同的就可以。

在標頭檔案中加入或修改 inline 函式時,使用了該標頭檔案的所有原始檔都必須重新編譯。

4.  慎用內聯

內聯雖有它的好處,但是也要慎用,以下摘自《高質量程式設計指南——C++/C語言》:

而在Google C++編碼規範中則規定得更加明確和詳細:

行內函數:

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

定義: 當函式被宣告為行內函數之後, 編譯器會將其內聯展開, 而不是按通常的函式呼叫機制進行呼叫.
優點: 當函式體比較小的時候, 內聯該函式可以令目的碼更加高效. 對於存取函式以及其它函式體比較短, 效能關鍵的函式, 鼓勵使用內聯.
缺點: 濫用內聯將導致程式變慢. 內聯可能使目的碼量或增或減, 這取決於行內函數的大小. 內聯非常短小的存取函式通常會減少程式碼大小, 但內聯一個相當大的函式將戲劇性的增加程式碼大小. 現代處理器由於更好的利用了指令快取, 小巧的程式碼往往執行更快。
結論: 一個較為合理的經驗準則是, 不要內聯超過 10 行的函式. 謹慎對待解構函式, 解構函式往往比其表面看起來要更長, 因為有隱含的成員和基類解構函式被呼叫!
另一個實用的經驗準則: 內聯那些包含迴圈或 switch 語句的函式常常是得不償失 (除非在大多數情況下, 這些迴圈或 switch 語句從不被執行).
有些函式即使宣告為內聯的也不一定會被編譯器內聯, 這點很重要; 比如虛擬函式和遞迴函式就不會被正常內聯. 通常, 遞迴函式不應該宣告成行內函數.(遞迴呼叫堆疊的展開並不像迴圈那麼簡單, 比如遞迴層數在編譯時可能是未知的, 大多數編譯器都不支援內聯遞迴函式). 虛擬函式內聯的主要原因則是想把它的函式體放在類定義內, 為了圖個方便, 抑或是當作文件描述其行為, 比如精短的存取函式.

-inl.h檔案:


Tip: 複雜的行內函數的定義, 應放在後綴名為 -inl.h 的標頭檔案中.


行內函數的定義必須放在標頭檔案中, 編譯器才能在呼叫點內聯展開定義. 然而, 實現程式碼理論上應該放在 .cc 檔案中, 我們不希望 .h 檔案中有太多實現程式碼, 除非在可讀性和效能上有明顯優勢.

如果行內函數的定義比較短小, 邏輯比較簡單, 實現程式碼放在 .h 檔案裡沒有任何問題. 比如, 存取函式的實現理所當然都應該放在類定義內. 出於編寫者和呼叫者的方便, 較複雜的行內函數也可以放到 .h 檔案中, 如果你覺得這樣會使標頭檔案顯得笨重, 也可以把它萃取到單獨的 -inl.h 中. 這樣把實現和類定義分離開來, 當需要時包含對應的 -inl.h 即可。

        </div>
            </div>

相關推薦

C的內外連結的區別

1.行內函數的內連結如inline static void fn(void) {} 沒有任何限制(建議使用) 2.行內函數的外連結如inline void fn(void) {} 則有諸多限制,最易被忽略的便是行內函數的外連結的定義(不僅需要.h檔案的替換體,還需要單獨的.c檔案存放extern&

C++ 摘自 C++ 應用程式效能優化

行內函數 在C++語言的設計中,行內函數的引入可以說完全是為了效能的考慮。因此在編寫對效能要求比較高的C++程式時,非常有必要仔細考量行內函數的使用。 所謂"內聯",即將被呼叫函式的函式體程式碼直接地整個插入到該函式被呼叫處,而不是通過call語句進行。當然,編譯器在真正進行"內聯"時,因為考慮到被行內函數

UE4 C++

#pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "Collecable.generated.h" U

C++】C++也可以用來代替巨集

巨集是可以帶引數的,它在形式上和函式非常相似。不過不像函式,巨集僅僅是字串替換,不是按值傳遞,所以在編寫巨集時要特別注意,一不小心可能就會踩坑。 #define SQ(y) y*y 當n = 9 時,SQ(n) = 81 如果把SQ(n)換成SQ(n+1)卻得不到1

C++ (講解的TM真好)【轉】

1.  行內函數 在C++中我們通常定義以下函式來求兩個整數的最大值: 複製程式碼 程式碼如下:  int max(int a, int b)  {   return a > b ? a : b;  }  為這麼一個小的操作定義一個函式的好處有: ① 閱讀和

(一)C++

行內函數:編譯器將函式程式碼替換函式呼叫,省去函式呼叫帶來的時間和空間開銷,增加程式碼段記憶體開銷。 適用於:函式呼叫的時間代價高於函式執行所需的時間代價,且函式被頻繁呼叫,程式碼簡短。 定義與宣告: 1> 函式宣告前加關鍵字 inline,一般省略原型宣告 2> 函式定義前加關鍵字 inline

C++

行內函數的宣告和定義應該寫在標頭檔案中 //主要CPP 1.h//標頭檔案 //行內函數的宣告和定義應該寫在標頭檔案中 inline int add(int a, int b) { return a + b; } //普通函式的宣告寫.h檔案,定義寫在.c

c++ 學習三 C++

     使用函式能夠避免將相同程式碼重寫多次的麻煩,還能減少可執行程式的體積,但也會帶來程式執行時間上的開銷。 函式呼叫在執行時,首先要在棧中為形參和區域性變數分配儲存空間,然後還要將實參的值複製給形參,接下來還要將函式的返回地址(該地址指明瞭函式執行結束後,

c++ 和constexper函式

行內函數 將函式宣告行內函數, 通常是在編譯器, 將它在呼叫點將函式“內聯展開”。 inline int len(const string& str) { return st

c++ (一看就懂)

1.行內函數 在C++中我們通常定義以下函式來求兩個整數的最大值: int max(int a, int b) { return a > b ? a : b; } 為這麼一個小的操作定義一個函式的好處有: ① 閱讀和理解函式 max 的

c++

1.  行內函數 在C++中我們通常定義以下函式來求兩個整數的最大值: 複製程式碼 程式碼如下: int max(int a, int b) {  return a > b ? a

C++和帶預設形參值的函式

#include<iostream>#include<iomanip>using namespace std;int getVolume(int length,int width=2,int height=3);int main(){const int X=10,Y=12,Z=15;c

c++與巨集的區別

c++行內函數 c++行內函數與常規函式的主要區別不在於編寫方式的不同。在於c++編譯器如何將它們組合到程式中。 執行到函式呼叫指令時,程式將立即儲存該指令的記憶體地址,變將函式複製到堆疊,跳到標記函式起點 的記憶體單元,執行函式程式碼,函式體執行完後跳到地址被儲存的指令處

C++

對於經常要使用的程式碼段,為了方便使用會將其封裝成函式。然而在呼叫函式時會建立棧幀,增加了額外的開銷。為了節省開銷,在C語言中會使用巨集替換。然而巨集具有一些缺點: 1)不能除錯; 2)由於巨集使用簡單的文字替換,對於有些情況,在同一個作用域中同一個巨集使用兩次會出現重定

4、【C++】靜態成員變數/靜態成員函式//友元函式/友元類/友元成員函式

一、靜態成員     我們可以使用 static 關鍵字來把類成員定義為靜態的。當我們宣告類的成員為靜態時,這意味著無論建立多少個類的物件,靜態成員都只有一個副本。     靜態成員在類的所有物件中是共享的。如果不存在其他的初始化語句,在建立第一個物件時,所有的靜態資料都會被初始化為

C/C++之巨集、和普通函式的區別

轉載:https://www.cnblogs.com/ht-927/p/4726570.html C/C++之巨集、行內函數和普通函式的區別 行內函數的執行過程與帶引數巨集定義很相似,但引數的處理不同。帶引數的巨集定義並不對引數進行運算,而是直接替換;行內函數首先是函式,這就意味著函式的很多

C++關鍵字、名稱空間、函式過載、預設引數、、引用

一 .C++入門 1.C++關鍵字 2.名稱空間 3.C++輸入&輸出 4.預設引數 5.函式過載 6.引用 7.行內函數 8.auto關鍵字 9.基於範圍的for迴圈 10.指標空值nullptr&nullptr_t 二. 正文 1.C++關鍵字(C++98)   

C++知識點1(

1.  行內函數 在C++中我們通常定義以下函式來求兩個整數的最大值: 複製程式碼 程式碼如下: int max(int a, int b) {  return a > b ? a : b; } 為這麼一個小的操作定義一個函式的好處有: ①

C++中的,預設引數和佔位引數

說明:裡面的示例程式碼有些被註釋掉了,想執行時要去掉註釋 #include "iostream" using namespace std; /**      內聯函數出現的原因:     C++中的const常量可以替代巨

C語言 inline與帶參巨集

C語言 inline行內函數與帶參巨集 一、簡述         簡單的介紹inline行內函數、帶參巨集的作用。 二、函式的執行與呼叫         函式執行:會將之前的棧的頂,棧基址壓棧,並在棧中開