標籤:
代碼編譯運行環境:VS2012+Win32+Debug
1.名字空間的由來
名字空間(namespace)是由標準C++引入的,是一種新的範圍層級。原來C++標識符的範圍分為三級:代碼塊({…}和函數體)、類域和全域範圍。如今,在類範圍和全域範圍之間,C++標準又添加了名字空間域這一個範圍層級。
命名空間是ANSIC++引入的可以由使用者命名的範圍,用來處理常式中常見的同名衝突。
2.名字空間的作用
名字空間的作用主要是為瞭解決日益嚴重的名稱衝突問題。隨著可重用代碼的增多,各種不同的代碼體系中的標識符之間同名的情況就會顯著增多。解決的辦法就是將不同的程式碼程式庫放到不同的名字空間中。
訪問一個具體的標識符的時候,可以使用如下形式:space_name::identifier。即用範圍指示符“::”將名字空間的名稱和該空間下的標識符串連起來,這要,即使使用同名的標識符,由於它們處於不同的名字空間,也不會發生衝突。
有兩種形式的命名空間——有名的和無名的。
定義格式為:
有名的命名空間: namespace 命名空間名 { 聲明序列可選 }匿名的命名空間: namespace { 聲明序列可選 }
3.名字空間的注意要點
(1)一個名字空間可以在多個標頭檔或源檔案中實現,成為分段定義。如果想在當前檔案訪問定義在另一個檔案中的同名名字空間內的成員變數,需要在當前檔案的名字空間內部進行申明。如標準C++庫中的所有組件都是在一個被稱為std的名字空間中聲明和定義的。這些組件當然分散在不同的標頭檔和源檔案中。
(2)名字空間內部可以定義類型、函數、變數等內容,但名字空間不能定義在類和函數的內部。
(3)在一個名字空間中可以自由地訪問另一個名字空間的內容,因為名字空間並沒有保護層級的限制。
(4)雖然經常可以見到using namespace std;這樣的用法,我們也可以用同樣的方法將名字空間中的內容一次性“引入”到當前的名字空間中來,但這並不是一個值得推薦的用法。因為這樣做的相當於取消了名字空間的定義,使發生名稱衝突的機會增多。所以,用using單獨引入需要的內容,這樣會更有針對性。例如,要使用標準輸入對象,只需用using std::cin;就可以了。
(5)不能在名字空間的定義中聲明另一個嵌套的子命名空間,只能在命名空間中定義子命名空間。
(6)名字空間的成員,可以在命名空間的內部定義,也可以在名字空間的外部定義,但是要在名字空間進行聲明。
命名空間成員的外部定義的格式為:
名字空間名::成員名 ……
(7)名字空間在進行分段定義時,不能定義同名的變數,否則串連出現重定義錯誤。因為名字空間不同於類,具有外部串連的特性。由於外部串連特性,請不要將名字空間定義在標頭檔,因為當被不同的源檔案包含時,會出現重定義的錯誤。
結合以上幾點,觀察如下程式。
//main.cpp#include <iostream>namespace myspace1{ extern int gvar;//內部聲明 extern int otherVar; //另一個檔案中同名名字空間中定義 using std::cout; using std::endl; class myclass{ public: void print(){ cout<<"in space1,gvar="<<gvar<<endl; } };}namespace myspace2{ using std::cout; using std::endl; int i=5; class myclass{ public: void print(){ cout<<"in space2"<<endl; } }; namespace nestedspace{ void ExternFunc();//內部聲明 }}//外部定義int myspace1::gvar=1;void myspace2::nestedspace::ExternFunc(){ cout<<"in nestedspace"<<endl;}int main(int argc,char* argv[]){ myspace1::myclass obj1; obj1.print(); myspace2::myclass obj2; obj2.print(); myspace2::nestedspace::ExternFunc(); std::cout<<myspace1::otherVar<<std::endl; getchar();}//sp2.cppnamespace myspace1{ int otherVar=3;}
程式輸出結果是:
(8)為了避免命名空間的名字與其他的命名空間同名,可以用較長的標識符作為命名空間的名字。但是書寫較長的命名空間名時,有些冗餘,因此,我們可以在特定的上下文環境中給命名空間起一個相對簡單的別名。
參考如下程式。
namespace MyNewlyCreatedSpace{ void show(){ std::cout<<"a function within a namespace"<<std::endl; }}int main(int argc,char* argv[]){ namespace sp=MyNewlyCreatedSpace; sp::show();}
4.匿名名字空間4.1與static關鍵字的共同作用
匿名名字空間提供了類似在全域函數前加 static 修飾帶來的限制範圍的功能。它的這種特性可以被用在struct和class上, 而普通的static卻不能。比如,在兩個源檔案中定義了相同的全域變數(或函數),就會發生重定義的錯誤。如果將它們聲明為全域靜態變數(函數)就可以避免重定義錯誤。在C++中,除了可以使用static關鍵字避免全域變數(函數)的重定義錯誤,還可以通過匿名名字空間的方式實現。參考如下代碼。
//main.cpp#include <iostream>using namespace std;namespace{ double dvar=1.8;}void show1(){ cout<<"dvar:"<<dvar<<endl;}int main(int argc,char* argv[]){ void show2(); show1(); show2();}//a.cpp#include <iostream>using namespace std;double dvar=2.8;void show2(){ cout<<"dvar:"<<dvar<<endl;}
程式輸出:
未命名的名字空間中定義的變數(或函數)只在包含該名字空間的檔案中可見,但其中的變數的生存期卻從程式開始到程式結束。如果有多個檔案包含未命名的名字空間,這些名字空間是不相關的,即使這些名字空間中定義了同名的變數(函數),這些標識符也代表不同的對象。
4.2與static的不同
通過匿名名字空間,同樣實現了對不同源檔案中同名全域變數(函數)的保護,使它們不至於發生沖一定衝突。在這一點上,匿名名字空間和static的作用是相同的。
但是,用static修飾的變數(函數)具有內部串連特性,而具有內部串連特性的變數(函數)是不能用來執行個體化一個模板的。參考如下程式。
#include <iostream>using namespace std;template <char*p> class Example{public: void display(){ cout<<*p<<endl; }};static char c=‘a‘;int main(int argc,char* argv[]){ Example<&c> a; //編譯出錯 a.display();}
此程式無法通過編譯,因為靜態變數c不具有外部串連特性,因此不是真正的“全域”變數。而類模板的非型別參數要求是編譯時間常量運算式,或者是指標類型的參數要求指標指向的對象具有外部串連性。具體要求,參見C++標準關於模板非型別參數的要求:
14.3.2 Template non-type arguments [temp.arg.nontype]
1 A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— the name of a non-type template-parameter; or
— the address of an object or function with external linkage, including function templates and function
template-ids but excluding non-static class members, expressed as & id-expression where the & is
optional if the name refers to a function or array, or if the corresponding template-parameter is a reference;
or
— a pointer to member expressed as described in 5.3.1 .
C++11標準文檔的下載見C++11標準文檔下載,或者到官網下載:ISO相關標準官網。
為了實現技能保護全域變數(函數)不受重定義錯誤的幹擾,能夠使它們具有外部串連特性的目的,必須使用匿名名字空間機制。同樣是上面的這個程式,將char c=’a’;
至於匿名空間進行定義,即可通過編譯並運行。讀者可自行考證。
通過以上程式,可以看出匿名名字空間與static的區別:包含在匿名名字空間中的全域變數(函數)具有外部串連特性,而用static修飾的全域變數具有內部串連特性,不能用來執行個體化模板的非型別參數。
參考文獻
[1] C++進階進階教程.陳剛.武漢大學出版社
[2]http://zhidao.baidu.com/link?url=1XLWBRwm0DpflKl9wM0DrjDFN1tY_gvl_c6DUpsHKjteRF-_IUCiAfYsXi21glTgFh1cfuwk0dngzz7Px6YJQa
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
C++名字空間詳解