inline用於替代巨集函式

2022-06-21 08:30:12 字數 3985 閱讀 9108

在c&c++中

一、inline關鍵字用來定義乙個類的內聯函式,引入它的主要原因是用它替代c中表示式形式的巨集定義。

表示式形式的巨集定義一例:

#define expressionname(var1,var2) ((var1)+(var2))*((var1)-(var2))

取代這種形式的原因如下:

1. c中使用define這種形式巨集定義的原因是因為,c語言是乙個效率很高的語言,這種巨集定義在形式及使用上像乙個函式,但它使用預處理器實現,沒有了引數壓棧,**生成等一系列的操作,因此,效率很高,這是它在c中被使用的乙個主要原因。

2. 這種巨集定義在形式上類似於乙個函式,但在使用它時,僅僅只是做預處理器符號表中的簡單替換,因此它不能進行引數有效性的檢測,也就不能享受c++編譯器嚴格型別檢查的好處,另外它的返回值也不能被強制轉換為可轉換的合適的型別,這樣,它的使用就存在著一系列的隱患和侷限性。

3. 在c++中引入了類及類的訪問控制,這樣,如果乙個操作或者說乙個表示式涉及到類的保護成員或私有成員,你就不可能使用這種巨集定義來實現(因為無法將this指標放在合適的位置)。

4. inline 推出的目的,也正是為了取代這種表示式形式的巨集定義,它消除了巨集定義的缺點,同時又很好地繼承了巨集定義的優點。

預定義

對應於上面的1-3點,闡述如下:

1. inline 定義的類的內聯函式,函式的**被放入符號表中,在使用時直接進行替換,(像巨集一樣展開),沒有了呼叫的開銷,效率也很高。

2. 很明顯,類的內聯函式也是乙個真正的函式,編譯器在呼叫乙個內聯函式時,會首先檢查它的引數的型別,保證呼叫正確。然後進行一系列的相關檢查,就像對待任何乙個真正的函式一樣。這樣就消除了它的隱患和侷限性。

3. inline 可以作為某個類的成員函式,當然就可以在其中使用所在類的保護成員及私有成員。

在何時使用inline函式

首先,你可以使用inline函式完全取代表示式形式的巨集定義。

另外要注意,內聯函式一般只會用在函式內容非常簡單的時候,這是因為,內聯函式的**會在任何呼叫它的地方展開,如果函式太複雜,**膨脹帶來的惡果很可能會大於效率的提高帶來的益處。內聯函式最重要的使用地方是用於類的訪問函式。

一inline函式(摘自c++ primer的第三版)

在函式宣告或定義中函式返回型別前加上關鍵字inline即把min()指定為內聯。

inline int min(int first, int secend) ;

inline函式對編譯器而言必須是可見的,以便它能夠在呼叫點內展開該函式。與非inline函式不同的是,inline函式必須在呼叫該函式的每個文字檔案中定義。當然,對於同一程式的不同檔案,如果inline函式出現的話,其定義必須相同。對於由兩個檔案compute.c和draw.c構成的程式來說,程式設計師不能定義這樣的min()函式,它在compute.c中指一件事情,而在draw.c中指另外一件事情。如果兩個定義不相同,程式將會有未定義的行為.

為保證不會發生這樣的事情,建議把inline函式的定義放到標頭檔案中。在每個呼叫該inline函式的檔案中包含該標頭檔案。這種方法保證對每個inline函式只有乙個定義,且程式設計師無需複製**,並且不可能在程式的生命期中引起無意的不匹配的事情。

二內聯函式的程式設計風格(摘自高質量c++/c 程式設計指南)

關鍵字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 與函式定義體放在一起

所以說,inline 是一種「用於實現的關鍵字」,而不是一種「用於宣告的關鍵字」。一般地,使用者可以閱讀函式的宣告,但是看不到函式的定義。儘管在大多數教科書中內聯函式的宣告、定義體前面都加了inline 關鍵字,但我認為inline 不應該出現在函式的宣告中。這個細節雖然不會影響函式的功能,但是體現了高質量c++/c

程式設計風格的乙個基本原則:宣告與定義不可混為一談,使用者沒有必要、也不應該知道函式是否需要內聯。

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

class a

// 自動地成為內聯函式

}將成員函式的定義體放在類宣告之中雖然能帶來書寫上的方便,但不是一種良好的程式設計

風格,上例應該改成:

// 標頭檔案

class a

// 定義檔案

inline void a::foo(int x, int y)

慎用內聯

內聯能提高函式的執行效率,為什麼不把所有的函式都定義成內聯函式?

如果所有的函式都是內聯函式,還用得著「內聯」這個關鍵字嗎?

內聯是以**膨脹(複製)為代價,僅僅省去了函式呼叫的開銷,從而提高函式的執行效率。如果執行函式體內**的時間,相比於函式呼叫的開銷較大,那麼效率的收穫會很少。另一方面,每一處內聯函式的呼叫都要複製**,將使程式的總**量增大,消耗更多的記憶體空間。以下情況不宜使用內聯:

(1)如果函式體內的**比較長,使用內聯將導致記憶體消耗代價較高。

(2)如果函式體內出現迴圈,那麼執行函式體內**的時間要比函式呼叫的開銷大。

類的建構函式和析構函式容易讓人誤解成使用內聯更有效。要當心建構函式和析構函式可能會隱藏一些行為,如「偷偷地」執行了基類或成員物件的建構函式和析構函式。所以不要隨便地將建構函式和析構函式的定義體放在類宣告中。乙個好的編譯器將會根據函式的定義體,自動地取消不值得的內聯(這進一步說明了inline 不應該出現在函式的宣告中)。

c++語言支援函式內聯,其目的是為了提高函式的執行效率(速度)。

在c程式中,可以用巨集**提高執行效率。巨集**本身不是函式,但使用起來象函式。

預處理器用複製巨集**的方式代替函式呼叫,省去了引數壓棧、生成組合語言的call呼叫、 返回引數、執行return等過程,從而提高了速度。 

使用巨集**最大的缺點是容易出錯,預處理器在複製巨集**時常常產生意想不到的邊際效應。 

對於c++ 而言,使用巨集**還有另一種缺點:無法操作類的私有資料成員。 

讓我們看看c++ 的"函式內聯"是如何工作的。 

對於任何內聯函式,編譯器在符號表裡放入函式的宣告(包括名字、引數型別、返回值型別)。 如果編譯器沒有發現內聯函式存在錯誤,那麼該函式的**也被放入符號表裡。 在呼叫乙個內聯函式時,編譯器首先檢查呼叫是否正確 (進行型別安全檢查,或者進行自動型別轉換,當然對所有的函式都一樣)。如果正確,內聯函式的**就會直接替換函式呼叫,於是省去了函式呼叫的開銷。這個過程與預處理有顯著的不同,因為預處理器不能進行型別安全檢查,或者進行自動型別轉換。 假如內聯函式是成員函式,物件的位址(this)會被放在合適的地方,這也是預處理器辦不到的。

c++語言的函式內聯機制既具備巨集**的效率,又增加了安全性,而且可以自由操作類的資料成員。 所以在c++ 程式中,應該用內聯函式取代所有巨集**,"斷言assert"恐怕是唯一的例外。

assert是僅在debug版本起作用的巨集,它用於檢查"不應該"發生的情況。

為了不在程式的debug版本和release版本引起差別,assert不應該產生任何***。

如果assert是函式,由於函式呼叫會引起記憶體、**的變動,那麼將導致debug版本與release版本存在差異。 所以assert不是函式,而是巨集。

--------------------------------------- 來自:inline用法詳解

inline 函式和巨集的比較

define table comp x x 0?x 0 就定義了乙個巨集。為什麼要使用巨集呢?因為函式的呼叫必須要將程式執行的順序轉移到函式所存放 在記憶體中的某個位址,將函式的程式內容執行完後,再返回到轉去執行該函式前的地 方。這種轉移操作要求在轉去執行前要儲存現場並記憶執行的位址,轉回後要恢復現...

從巨集定義到inline函式

一 巨集和普通函式 在ti提供的一些例程中,經常能看到像下面這種使用巨集的方式。巨集經常用於簡單的計算,比如求兩個數的最大值或者最小值。define max a,b a b a b c和指標 一書指出這樣做的好處有兩個 第一,用於呼叫和從函式返回的 很可能比實際執行這個小型計算工作的 更大 博文作者...

define 巨集 替代巨集

編寫 時經常要定義常量 static const nstimeinterval kanimationduration 0.3 複製 使用型別變數,可以清楚地描述常量的含義,以及它的型別。命名法 若常量侷限於某 編譯單元 translation unit,也就是實現檔案,m檔案 之內,則在前面加字母k...