用內聯取代巨集:
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 巨集...