有關內聯函式的一些事

2021-05-10 13:23:49 字數 1771 閱讀 8228

有關內聯函式的一些事,《thinking in c++》和《effective c++》的學習筆記。

內聯函式是c++用來替換巨集而引入的。

c中的巨集在省去函式呼叫的開銷的同時引入了不易發現的bug,主要是由對引數求值引起的。

一、內聯函式如何起作用:

對於普通函式,編譯器只把函式名稱(對於c++來說也包含了引數型別?)和返回值記錄在符號表裡,對於內聯函式除此之外還在符號表裡記錄其函式體(究竟存放源**還是編譯後的彙編指令就看編譯器的實現了)。當遇到內聯函式的呼叫時,編譯器首先檢查呼叫是否正確(引數型別檢查,返回結果是否被正確使用——對於普通函式也進行這些檢查),檢查無誤後將內聯函式的函式體替換掉對它的呼叫,從而省去呼叫函式的開銷(引數入棧,彙編call等)。

二、如何讓函式成為內聯:

使用關鍵字inline指定函式為內聯。

宣告函式為內聯是沒有意義也沒有必要的,要讓函式成為內聯需要在定義時指定。

對於類的成員函式有一些特殊,在類的宣告內定義的成員函式自動成為內聯,在類體外定義的函式就需要顯式指定了。

由於內聯函式的定義必須被包含在每乙個使用它的檔案裡,而定義的不統一又會造成多重定義的error,因此將內聯函式的定義放在標頭檔案裡是合適的。

將內聯函式的定義放在標頭檔案裡的根本原因是,大多數建制環境都是在編譯期執行內聯的,而編譯期要將函式呼叫替代為函式體,就需要指導函式長什麼樣。

——>擴充套件:

這點也和function template相同,函式模板也通常放在標頭檔案裡。這是因為大多數編譯器在編譯期進行函式模板的具現化,因此它需要知道函式模板長什麼樣子。

所以不應該有「函式模板都是inline」的推論。

三、編譯器和內聯函式:

有幾種情況編譯器無法執行函式的內聯,遇到這種情況,即使你將函式定義為內聯,編譯器仍然會將其按照普通函式處理——為函式體分配儲存空間。如果這必須在多個編譯單元之間進行,這很可能導致多重定義錯誤,若一定要執行的話(取決於編譯器),鏈結器會被告知忽略多重定義。下面列出這些情況:

1)函式過於複雜。這取決於編譯器,但大多數編譯器都會選擇放棄,這時候即使內聯你也得不到任何顯著的效率提公升。一般,函式體內帶有迴圈或者遞迴,都會被認為過於複雜。

2)函式被取址時。這就迫使編譯器不得不為其分配記憶體從而產生乙個位址,然而在不被取址的地方,內聯仍有可能被執行。

另外,對virtual函式的呼叫也做不到inlining,因為virtual意味著「等待,直到執行期才知道呼叫哪個函式」,因此編譯器無法知道呼叫的具體函式,也就沒法進行**替換。

乙個不應存在的顧慮:

由於類的宣告中給出函式的定義時,可能類的全部成員函式沒有都完成宣告,所以可能會有這樣的顧慮:

class a

int f() const

int g() const

};main()

f()定義內呼叫了g(),而此時g()還沒有宣告。

其實這種顧慮是不必要的,因為語言定義規定在類中所有內聯函式的評估都要在類宣告完成時進行。

四、何時使用內聯:

內聯肯定會提高程式的效率,儘管有時這種提高微乎其微。

內聯不一定會帶來**膨脹,如果函式體十分短小,甚至比呼叫函式所生成的**都小,那就不會**膨脹,反而會縮小object code。

這給何時使用內聯提供了指導。

inline還有另乙個需要考慮的成本:

對於程式開發者,公升級乙個inline的函式比公升級乙個outline的函式成本更大,因為inline函式定義在標頭檔案中,因此更新後所有使用到此函式的客戶檔案都需要重新編譯,而對於outline函式,則只需要重新鏈結即可。如果是動態鏈結,甚至沒有成本。

——the end

有關內聯函式的總結

原因 對於函式的呼叫,開銷較大,但是使用inline內聯的時候,呼叫函式的時候,編譯器會將 副本展開到函式呼叫處,所以,這樣子程式的 規模會比較大,但是能減少函式呼叫開銷,提高效能。a 由程式設計師在函式中加inline關鍵字,是 建議 編譯器將函式內聯,編譯器保留是否內聯的最終權利,一般較小的函式...

OpenCV 有關記憶體釋放的一些問題

前一天把系統整個重寫了一遍,脈絡清晰了很多,也終於解決了以前很多崩潰,異常退出的問題。這裡小小總結一下自己遇到的麻煩。記憶體洩露是說沒有釋放已經不能使用的記憶體,這裡一般指堆的記憶體才需要顯示的釋放。比如用malloc,calloc,realloc,new分配的記憶體是在堆上的,需要用free,de...

hierarchyid有關的一些函式

於hierarchyid有關的一些函式主要有 getancestor 取得某乙個級別的祖先 getdescendant 取得某乙個級別的子代 getlevel 取得級別 getroot 取得根 isdescendantof 判斷某個節點是否為某個節點的子代 parse 將字串轉換為hierarchy...