C 內聯函式與巨集定義

2022-07-18 09:09:10 字數 4799 閱讀 7140

用內聯取代巨集:

1.內聯可除錯;

2.可進行型別安全檢查或自動型別轉換;

3.可訪問成員變數。

另外,定義在類宣告中的成員函式自動轉化為內聯函式。

文章(一)

內聯函式與巨集定義

在c中,常用預處理語句#define來代替乙個函式定義。例如:

#define max(a,b) ((a)>(b)?(a):(b))

該語句使得程式中每個出現max(a,b)函式呼叫的地方都被巨集定義中後面的表示式((a)>(b)?(a):(b))所替換。

巨集定義語句的書寫格式有過分的講究, max與括號之間不能有空格,所有的引數都要

放在括號裡。儘管如此,它還是有麻煩:

int a=1,b=0;

max(a++,b); //a被增值2次

max(a++,b+10); //a被增值1次

max(a,"hello"); //錯誤地比較int和字串,沒有引數型別檢查

max( )函式的求值會由於兩個引數值的大小不同而產生不同的***。

max(a++,b)的值為2,同時a的值為3;

max(a++,b+10)的值為10,同時a的值為2。

如果是普通函式,則max(a,"hello")會受到函式呼叫的檢查,但此處不會因為兩個引數型別不同而被編譯拒之門外。幸運的是,通過乙個內聯函式可以得到所有巨集的替換效能和 所有可預見的狀態以及常規函式的型別檢查:

inline int max(int a,int b)

1.內聯函式與巨集的區別:

傳統的巨集定義函式可能會引起一些麻煩。

ex:#define f(x) x+x

void main()

這裡x將被加兩次。

內聯函式被編譯器自動的用函式的形勢新增進**,而不會出現這種情況。

內聯函式的使用提高了效率(省去了很多函式呼叫彙編**如:call和ret等)。

2.內聯函式的使用:

所有在類的宣告中定義的函式將被自動認為是內聯函式。

class a()

void c();// not a inline function;

void d()

如果想將乙個全域性函式定義為內聯函式可用,inline 關鍵字。

inline a()

注意:在內聯函式中如果有複雜操作將不被內聯。如:迴圈和遞迴呼叫。

總結:將簡單短小的函式定義為內聯函式將會提高效率。

文章(二)

8.5.1 用內聯取代巨集** 

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

在 c程式中,可以用巨集**提高執行效率。巨集**本身不是函式,但使用起來象函式。預處理器用複製巨集**的方式代替函式呼叫,省去了引數壓棧、生成組合語言的 call呼叫、返回引數、執行return等過程,從而提高了速度。使用巨集**最大的缺點是容易出錯,預處理器在複製巨集**時常常產生意想不到的邊際效 應。例如 

#define max(a, b)       (a) > (b) ? (a) : (b) 

語句  

result = max(i, j) + 2 ; 

將被預處理器解釋為 

result = (i) > (j) ? (i) : (j) + 2 ; 

由於運算子『+』比運算子『:』的優先順序高,所以上述語句並不等價於期望的 

result = ( (i) > (j) ? (i) : (j) ) + 2 ; 

如果把巨集**改寫為 

#define max(a, b)       ( (a) > (b) ? (a) : (b) ) 

則可以解決由優先順序引起的錯誤。但是即使使用修改後的巨集**也不是萬無一失的,例如語句 

result = max(i++, j); 

將被預處理器解釋為 

result = (i++) > (j) ? (i++) : (j); 

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

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

c++ 語言的函式內聯機制既具備巨集**的效率,又增加了安全性,而且可以自由操作類的資料成員。所以在c++ 程式中,應該用內聯函式取代所有巨集**,「斷言assert」恐怕是唯一的例外。assert是僅在debug版本起作用的巨集,它用於檢查「不應該」發生 的情況。為了不在程式的debug版本和release版本引起差別,assert不應該產生任何***。如果assert是函式,由於函式呼叫會引起內 存、**的變動,那麼將導致debug版本與release版本存在差異。所以assert不是函式,而是巨集。(參見6.5節「使用斷言」)

8.5.2 內聯函式的程式設計風格 

關鍵字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) 

8.5.3 慎用內聯 

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

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

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

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

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

類的建構函式和析構函式容易讓人誤解成使用內聯更有效。要當心建構函式和析構函式可能會隱藏一些行為,如「偷偷地」執行了基類或成員物件的建構函式和析構函式。所以不要隨便地將建構函式和析構函式的定義體放在類宣告中。 

乙個好的編譯器將會根據函式的定義體,自動地取消不值得的內聯(這進一步說明了inline不應該出現在函式的宣告中)。 

8.6 一些心得體會 

c++ 語言中的過載、內聯、預設引數、隱式轉換等機制展現了很多優點,但是這些優點的背後都隱藏著一些隱患。正如人們的飲食,少食和暴食都不可取,應當恰到好 處。我們要辨證地看待c++的新機制,應該恰如其分地使用它們。雖然這會使我們程式設計時多費一些心思,少了一些痛快,但這才是程式設計的藝術。

第9章 類的建構函式、析構函式與賦值函式 

建構函式、析構函式與賦值函式是每個類最基本的函式。它們太普通以致讓人容易麻痺大意,其實這些貌似簡單的函式就象沒有頂蓋的下水道那樣危險。 

每個類只有乙個析構函式和乙個賦值函式,但可以有多個建構函式(包含乙個拷貝建構函式,其它的稱為普通建構函式)。對於任意乙個類a,如果不想編寫上述函式,c++編譯器將自動為a產生四個預設的函式,如 

a(void); // 預設的無引數建構函式 

a(const a &a); // 預設的拷貝建構函式 

~a(void); // 預設的析構函式 

a & operate =(const a &a); // 預設的賦值函式

這不禁讓人疑惑,既然能自動生成函式,為什麼還要程式設計師編寫? 

原因如下: 

(1)如果使用「預設的無引數建構函式」和「預設的析構函式」,等於放棄了自主「初始化」和「清除」的機會,c++發明人stroustrup的好心好意白費了。 

(2)「預設的拷貝建構函式」和「預設的賦值函式」均採用「位拷貝」而非「值拷貝」的方式來實現,倘若類中含有指標變數,這兩個函式注定將出錯。

對於那些沒有吃夠苦頭的c++程式設計師,如果他說編寫建構函式、析構函式與賦值函式很容易,可以不用動腦筋,表明他的認識還比較膚淺,水平有待於提高。 

本章以類string的設計與實現為例,深入闡述被很多教科書忽視了的道理。string的結構如下: 

class string 

;

內聯函式與巨集定義

用內聯取代巨集 1.內聯可除錯 2.可進行型別安全檢查或自動型別轉換 3.可訪問成員變數。另外,定義在類宣告中的成員函式自動轉化為內聯函式。內聯函式與巨集定義 在c中,常用預處理語句 define來代替乙個函式定義。例如 define max a,b a b a b 該語句使得程式中每個出現max ...

內聯函式與巨集定義

1 內聯函式取消了引數的壓棧,減少呼叫的開銷 2 內聯函式宣告必須和定義一起,如果只有宣告,編譯器只會將它看做普通函式的宣告,如果宣告的時候使用inline,定義在其他地方也用inline,那麼它是內聯還是普通函式 普通函式。查彙編 內聯函式與普通函式一樣?3 c 類中定義的函式都預設是內聯函式,無...

巨集定義與內聯函式

1 巨集定義的規則和使用解析 1 巨集定義的解析規則就是 在預處理階段由預處理器進行替換,這個替換是原封不動的替換。2 巨集定義替換會遞迴進行,直到替換出來的值本身不再是乙個巨集為止。3 乙個正確的巨集定義式子本身分為3部分 第一部分是 dedine 第二部分是巨集名 剩下的所有為第三部分。4 巨集...