預備知識:c/c++編譯過程
step1:預處理
——由預處理器(乙個簡單的程式)將程式設計師通過預處理器指令(參考部落格:c/c++預處理器指令c/c++預處理指令)定義好的模式代替源**中的模式。
step2:第一遍編譯
——對預處理過的**進行語法分析,編譯器把源**分解成小的單元並把它們按樹形結構組織起來。比如表示式「a+b」,被分解成語法分析樹的葉子節點「a」、「b」和「+」。型別檢查也在這個階段進行,由於是在編譯階段而不是在執行時進行記憶體檢查,所以也稱為「靜態型別檢查」。
step3:第二遍編譯
——有**生成器遍歷語法分析樹,把樹的每個節點轉化成組合語言(再由彙編器對其進行彙編成機器碼)或者直接轉換成機器**,生成目標模組(.o或.obj檔案)。
step4:連線
——將彙編生成的目標**進行連線,生成可執行檔案。
1. 內聯函式與巨集定義
在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,"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)
兩者的比較:
由於省去了引數壓棧、生成組合語言的 call呼叫、返回引數、執行return等過程,從而提高了速度;都能節省輸入,提高**的可讀性。
巨集定義容易引起不必要的錯誤。
用內聯取代巨集具有如下優勢:
1).內聯可除錯;
2).可進行型別安全檢查或自動型別轉換;
3).可訪問成員變數;
4.) 內聯函式的使用提高了效率(省去了很多函式呼叫彙編**如:call和ret等)
。另外,定義在類宣告中的成員函式自動轉化為內聯函式。
值得注意的是,內聯是以**膨脹(複製)為代價,僅僅省去了函式呼叫的開銷,從而提高函式的執行效率。如果執行函式體內**的時間(如存在迴圈和遞迴呼叫),相比於函式呼叫的開銷較大,那麼效率的收穫會很少。另一方面,每一處內聯函式的呼叫都要複製**,將使程式的總**量增大,消耗更多的記憶體空間。以下情況不宜使用內聯:
(1)如果函式體內的**比較長,使用內聯將導致記憶體消耗代價較高。
(2)如果函式體內出現迴圈,那麼執行函式體內**的時間要比函式呼叫的開銷大。
注:要使函式成為內聯函式,在其定義處申明為inline才能湊效。
2.內聯函式的使用:
c++把所有在類的宣告中定義的函式自動認為是內聯函式。
class a()
}; 如果想將乙個全域性函式定義為內聯函式可用,inline 關鍵字。
inline a()
以下內容引自lchen_fhhls的部落格:
一些心得體會
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
;
內聯函式和巨集定義
在c中,我們常用巨集定義來達到優化速度的目的,但由於巨集定義的種種缺陷 大家應該都吃過這種苦吧 在c 中引入了內聯函式。內聯函式實現了巨集的概念,任何在類內定義的函式會自動的成為內聯函式,但是也可以在類外用inline關鍵字來定義內聯函式。內聯的目的和巨集一樣是為了減少函式呼叫的開銷,但是通常我們會...
巨集定義和內聯函式
巨集定義和內聯函式,都可以減少函式的呼叫開銷,每次呼叫函式不必壓棧和開闢新的空間。使用巨集定義和內聯函式 的執行效率高。它們的區別是 1 巨集定義是預編譯器載入,而內聯函式是由編譯器載入 2 巨集定義容易產生一些錯誤,define min x x x min 1 3 得到的結果不是我們想要的16,而...
內聯函式和巨集定義
1 容易出錯,預處理器在拷貝巨集 時,常常會產生意想不到的邊際效用,容易產生二義性。在呼叫處進行展開,會出現運算子優先順序的問題 2 不可以除錯。然而內聯函式在debug版本裡面,它根本不是真正的內斂,在release中才會成為真正的內斂。3 在c 中,巨集 無法操作類的私有資料成員。原因是,預處理...