C++程式設計思想筆記之四 (轉)

worldblog發表於2007-12-13
C++程式設計思想筆記之四 (轉)[@more@]

第09章  命名控制

一、來自C 語言中的靜態成員

在C 和C + +中,static都有兩種基本的含義,並且這兩種含義經常是互相有衝突的:1) 在固定的地址上分配,也就是說是在一個特殊的靜態資料區上建立的,而不是每次時在堆疊上產生的。這也是靜態的概念。
2) 對一個特定的編譯單位來說是本地的(就像我們在後面將要看到的,這在C++中包括類的範圍)。這裡static控制名字的可見性,所以這個名字在這個單元或類之外是不可見的。這也描述了連線的概念,它決定聯結器將看到哪些名字。

二、靜態物件的解構函式

靜態物件的解構函式(包括靜態儲存的所有物件,不僅僅是上例中的區域性靜態變數)在從main() 塊中退出時,或者標準的C 庫函式exit()被呼叫時才被呼叫。多數情況下main()函式的結尾也是呼叫exit()來結束程式的。這意味著在解構函式內部使用exit()是很危險的,因為這可能陷入一個死迴圈中。但如果用標準的C 庫函式abort()來退出程式,靜態物件的解構函式並不會被呼叫。

三、控制連線

一般情況下,在範圍內的所有名字(既不巢狀在類或函式中的名字)對程式中的所有編譯單元來說都是可見的。這就是所謂的外部連線,因為在連線時這個名字對聯結器來說是可見的,外部的編譯單元、全域性變數和普通函式都有外部連線。內部連線的一個好處是這個名字可以放在一個標頭檔案中而不用擔心連線時發生衝突。

那些通常放在標頭檔案裡的名字,像常量、行內函數(inline function ),在預設情況下都是內部連線的(當然常量只有在C + +中預設情況下是內部連線的,在C 中它預設為外部連線)。注意連線只引用那些在連線/裝載期間有地址的成員,因此類宣告和區域性變數並沒有連線。 

四、靜態成員函式

像靜態資料成員一樣,我們也可以建立一個靜態成員函式,它為類的全體服務而不是為一個類的部分物件服務。這樣就不需要定義一個全域性函式,減少了全域性或區域性名字空間的佔用,把這個函式移到了類的內部。當產生一個靜態成員函式時,也就表達了與一個特定類的聯絡。靜態成員函式不能訪問一般的資料成員,它只能訪問靜態資料成員,也只能呼叫其他的靜態成員函式。通常,當前物件的地址(this)是被隱含地傳遞到被呼叫的函式的。但一個靜態成員函式沒有this ,所以它無法訪問一般的成員函式。這樣使用靜態成員函式在速度上可以比全域性函式有少許的增長,它不僅沒有傳遞this所需的額外的花費,而且還有使函式在類內的好處。因為靜態成員物件的初始化方法,我們可以把上述類的一個靜態資料成員放到那個類的內部。

五、靜態初始化的依賴因素

有三種方法來處理這一問題:
1) 不用它,避免初始化時的互相依賴。這是最好的解決方法。
2) 如果實在要用,就把那些關鍵的靜態物件的定義放在一個檔案中,這樣我們只要讓它們在檔案中順序正確就可以保證它們正確的初始化。
3) 如果我們確信把靜態物件放在幾個編譯單元中是不可避免的(比方在編寫一個庫時,我們無法控制那些使用該庫的程式設計師)這時我們可用由Jerry Schwarz 在建立iostream庫(因為cin ,cout和cerr的定義是在不同的檔案中)時提供的一種技術。這一技術要求在庫標頭檔案中加上一個額外的類。這個類負責庫中靜態物件的動態初始化。(類似於Singleton)

六、轉換連線指定

如果C + +中編寫一個程式需要用到C 庫,那該怎麼辦呢?如果這樣宣告一個C 函式:
float f(int a,char b);  C++的就會將這個名字變成像_f_int_int之類的東西以支援函式過載(和型別連線)。然而,C編譯器編譯的庫一般不做這樣的轉換,所以它的內部名為_f。這樣,聯結器將無法解決我們C++對f()的呼叫。C++中提供了一個連線轉換指定,它是透過過載extern關鍵字來實現的。extern後跟一個字
符串來指定我們想宣告的函式的連線型別,後面是函式宣告。  extern "C" float f(int a,char b);

這就告訴編譯器f()是C連線,這樣就不會轉換函式名。標準的連線型別指定符有“C”和“C++”兩種,但編譯器開發商可選擇用同樣的方法支援其他語言。如果我們有一組轉換連線的宣告,可以把它們放在花括號:  extern "C" {  float f(int a); float g(int b);}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992811/,如需轉載,請註明出處,否則將追究法律責任。

相關文章