一、來自c中的靜態元素
在c和c++中,static有兩種基本含義(這兩種含義經常是互相衝突的):
1)在固定的位址上進行儲存分配,即物件是在乙個特殊的靜態資料區上建立的(靜態儲存);
2)對乙個特定的編譯單位來說是區域性的。
1、函式內部的靜態變數
c和c++允許函式內部定義乙個static物件,這個物件將儲存在程式的靜態資料區中,這個物件只在函式第一次呼叫時初始化一次,以後它將在兩次函式呼叫之間保持它的值。
零賦值只對內部型別有效,使用者自定義型別必須用建構函式來初始化。因此,如果在定義乙個靜態物件時沒有指定構造函式引數,這個類就必須有預設的建構函式。
靜態物件的析構函式(包括靜態儲存的所有物件)在程式從main()中退出時,或標準c的exit()被呼叫時才被呼叫。多數情況下main()的結尾也是呼叫exit()來結束程式的,這意味著如果在析構函式內部使用exit()將會導致無窮的遞迴。但如果用標準c的abort()來退出程式,靜態物件的析構函式並不會被呼叫。
靜態物件的銷毀也是按照與初始化時相反的順序進行的。
二、c++中的靜態成員
類的靜態成員擁有一塊單獨的儲存區,不管建立了多少個該類的物件,所有這些物件的靜態資料成員都共享這一塊靜態儲存空間(這就為這些物件提供了一種互相通訊的方法),但靜態資料屬於類,它的名字只在類的範圍內有效,可以是public、private、protected。
如果乙個靜態資料成員被宣告但沒有定義時,聯結器會報告乙個錯誤。定義必須出現在類的外部(不允許內聯)而且只能定義一次,因此它通常放在乙個類的實現檔案中。例如:
class a ;
之後,必須在定義檔案中為靜態資料成員定義儲存區:
int a::param = 1;
靜態資料成員可以放在另乙個類的巢狀類中,但是不能在區域性類(在函式內部定義的類)中有靜態資料成員。
當在乙個類中看到靜態成員函式時,要記住:類的設計者是想把這些函式與整個類在概念上關聯起來。靜態成員函式不能訪問一般的資料成員,只能訪問靜態資料成員,也只能呼叫其他的靜態成員函式。通常,當前物件的位址是被隱式地傳遞到被呼叫的函式的,但是靜態成員函式沒有this,所以它無法訪問一般的成員(非靜態成員函式可以訪問靜態成員函式和變數)。靜態函式意味著對這個函式的所有呼叫來說,乙個區域性變數只有乙份拷貝。
三、靜態初始化的相依性
在乙個指定的翻譯單元中,靜態物件的初始化順序嚴格按照物件在該單元中定義出現的順序,而清除的順序則與初始化的順序正好相反。但是,對於作用域為多個翻譯單元的靜態物件來說,不能保證嚴格的初始化順序,也沒有辦法來指定這種順序。
相互依賴的靜態物件的初始化,可能會導致程式異常,有三種方法來處理這一問題:
1)不用它,避免初始化時的相互依賴(最好的解決辦法);
2)如果實在要用,就把那些關鍵的靜態物件的定義放在乙個檔案中,這一只要讓它們在檔案中順序正確就可以保證它們正確的初始化;
3)如果確信把靜態物件放在幾個不同的翻譯單元中是不可避免的(如在編寫乙個庫時),通過兩種程式設計技術加以解決:a、在庫標頭檔案中加上乙個額外的類,這個類負責庫中的靜態物件的動態初始化。b、把乙個靜態物件放在乙個能返回物件引用的函式中(使用這種方法,訪問靜態物件的唯一途徑就是呼叫這個函式,函式第一次被呼叫時,它強迫初始化發生。靜態初始化的正確順序是由設計的**而不是由聯結器任意指定順序來保證的)。
不能把定義了靜態物件的函式作為內聯函式,這是因為內聯函式在它出現的每乙個檔案中都會有乙份副本(這種副本包括靜態物件的定義),而且內聯函式自動地預設為內部連線,所以這將導致多個重複的靜態物件,且它們作用域為多個編譯單元。
四、名字空間
c++的名字空間(namespace)特徵,把乙個全域性名字空間分成多個可管控的小空間。建立乙個名字空間與建立乙個類非常相似:
namespace mynamespace
注意:
1)namespace只能在全域性範圍內定義,但它們之間可以互相巢狀;
2)在namespace定義的結尾,右花括號的後面不必跟乙個分號;
3)乙個namespace可以在多個標頭檔案中用乙個識別符號來定義,就好像重複定義乙個類一樣。
4)乙個namespace可以用另乙個名字來作它的別名(如namespace mn = mynamespace;);
5)不能像類那樣去建立乙個名字空間的例項;
6)每個翻譯單元都可以包含乙個未命名的名字空間(最多只能有乙個),在這個空間中的變數和函式自動地在翻譯單元內有效(不需要加上static說明就可以讓它們作為內部連線);
7)可以在乙個名字空間的類定義內插入乙個友元宣告。
3、使用名字空間
在乙個名字空間中引用乙個名字可以採取三種方法:
1)用作用域運算子;
2)使用using指令把所有名字引入到名字空間中(使用using指令可能會引入帶名字衝突的名字空間);
3)用using宣告一次性引用名字(using宣告給出了識別符號的完整名字,但沒有型別方面的資訊,即如果名字空間中包含了一組用相同名字過載的函式,using宣告就宣告了這個過載的集合內的所有函式),可以把using宣告放在任何一般的宣告可以出現的地方。using宣告與普通宣告只有一點不同:using宣告可以引起乙個函式用相同的引數型別來過載(這在一般的過載中是不允許的)。如果不想由於引入不同的名字空間而導致重複定義乙個函式時,可以使用using宣告,它不會引起任何二義性和重複。
不要把乙個全域性的using指令引入到乙個標頭檔案中,因為那將意味著包含這個標頭檔案的任何其他檔案也會開啟這個名字空間。
五、替代連線說明
c++中提供了乙個替代連線說明,它是通過過載extern關鍵字來實現的。extern後跟乙個字串來指定想宣告的函式的連線型別,後面是函式宣告。如:
extern 「c」 float f(int a, char b);
這就告訴編譯器f()是c連線,這樣就不會轉換函式名。標準的連線型別指定符有「c」和「c++」兩種,但編譯器開發商可選擇用同樣的方法支援其他語言。
如果有一組替代連線的宣告,可以把它們放在花括號內:
extern 「c」
或在標頭檔案中:
extern 「c」
c 名字控制
如果內部型別的靜態變數未初始化,編譯器會將其初始化為零。但使用者自定義型別必須使用建構函式來初始化。內部連線的名字可以放在乙個標頭檔案中而不擔心鏈結時發生衝突。如通常放在標頭檔案中的static const c 預設內部連線 c預設外部連線 內聯函式在預設的情況下是內部連線的。全域性物件都是隱含為靜...
06 C 名字控制
建立名字是程式設計過程中一項最基本的活動,當乙個專案很大時,它會不可避免地包含大量名字。c 允許我們對名字的產生和名字的可見性進行控制。我們之前在學習c語言可以通過static關鍵字來使得名字只得在本編譯單元內可見,在c 中我們將通過一種通過命名空間來控制對名字的訪問。在c 中,名稱 name 可以...
C 名字修飾
名字修飾 name mangling 是一種在編譯過程中,將函式 變數的名稱重新改編的機制,簡單來說就是編譯器為了區分各個函式,將函式通過一定演算法,重新修飾為乙個全域性唯一的名稱。為什麼c語言不支援函式過載?下面來驗證一下 在c專案中輸入如下 int add int left,int right ...