0、序言 名字空間是C++提供的一種解決符號名字沖突的方法。 一個命令空間是一個作用域,在不同名字空間中命名相同的符號代表不同的實體。 通常,利用定義名字空間的辦法,可以使模塊劃分更加方便,減少模塊間的相互影響。 1、名字空間的成員 定義在名字空間中的實體稱為名字空間的成員。 名字空間內的名字可以被該名字空間內的其他成員直接訪問,名字空間外的代碼必須指定該名字位於哪個名字空間。 一個名字空間可以包含多種類型的標識符,如下面所列: 變量、常量、函數、結構體/聯合體/枚舉、類、嵌套名字空間 名字空間成員的引用方法如下: namespace_name::member_name 2、定義名字空間 (1)、一個名字空間可以在兩個地方被定義:在全局范圍層次或者是在另一個名字空間中被定義(這樣就形成一個嵌套名字空間),不能在函數和類的內部定義。 (2)、名字空間可以是不連續的,他是由所有分離定義的部分的總體構成的。一個名字空間可以分散多個文件中,不同的文件中名字空間的定義也是累積的。 通常將名字空間的聲明放到頭文件中,實現放到源文件中。可以將不相關的成員放到不同的頭文件中。 (3)、命令空間的作用域不能以分號結尾。 3、嵌套名字空間(Nested Namespce) 3.1、普通嵌套名字空間(ordinary nested namespace) 一個嵌套名字空間就是一個嵌套作用域,其作用域嵌套在包含他的名字空間中。 在外部引用嵌套空間中的成員時,使用下面的形式 包含嵌套空間的名字空間的名字::嵌套空間的名字::嵌套空間的成員 下面舉例說明嵌套名字空間定義和使用 復制代碼 #include <iostream> namespace MyOutNames { int iVal1 = 100; int iVal2 = 200; namespace MyInnerNames //定義嵌套名字空間 { int iVal3 = 300; int iVal4 = 400; } } int main(void) { std::cout<<MyOutNames::iVal1<<std::endl; std::cout<<MyOutNames::iVal2<<std::endl; std::cout<<MyOutNames::MyInnerNames::iVal3<<std::endl; //使用嵌套名字空間成員 std::cout<<MyOutNames::MyInnerNames::iVal4<<std::endl; //使用嵌套名字空間成員 return 0; } 復制代碼 3.2、內聯嵌套名字空間(Inline Namespace C++11) C++11中,新增inline namespace,指示命名空間中的名稱同時是外層命名空間直接包含的名稱。這便於命名空間的版本管理,減少沖突。 inline描述符使得內聯命名空間中的聲明看起來就好像是直接在外圍的命名空間中進行聲明的一樣。(使用inline關鍵字定義的內聯名字空間成為默認名字空間。) inline描述符由命名空間的設計者放置,即命名空間的作者可以通過放置inline描述符來表示當前最新的命名空間是哪個. 復制代碼 // file V98.hpp: namespace V98 { void f(int); // does something // ... } // file V99.hpp: inline namespace V99 { void f(int); // does something better than the V98 version void f(double); // new feature // ... } // file Mine.hpp: namespace Mine { #include "V99.hpp" #include "V98.hpp" } //file example.cpp #include "Mine.hpp" using namespace Mine; // ... V98::f(1); // old version V99::f(1); // new version f(1); //default version 復制代碼 4、全局名字空間(Global Namespce) 定義在全局作用域的名字(在任意類、函數或命名空間外部聲明的名字)是定義在全局命名空間中的。 全局命名空間是隱式聲明的,存在於每個程序中。在全局作用域定義實體的每個文件將那些名字加到全局命名空間。 可以用作用域操作符引用全局命名空間的成員。因為全局命名空間是隱含的,它沒有名字, 所以使用記號如下方法引用全局命名空間的成員。 ::member_name 5、匿名名字空間(Unnamed Namespace) 命名空間可以是未命名的,未命名的命名空間在定義時沒有給定名字。其定義方法如下: namespace //No name { //members.... } 未命名的命名空間與其他命名空間不同,未命名的命名空間的定義局部於特定文件,從不跨越多個文本文件。 未命名的命名空間可以在給定文件中不連續,但不能跨越文件,每個文件有自己的未命名的命名空間。 未命名的命名空間用於聲明局部於文件的實體。在未命名的命名空間中定義的變量在程序開始時創建,在程序結束之前一直存在。 未命名的命名空間中定義的名字可直接使用,因為沒有命名空間名字來限定它們。 復制代碼 #include <iostream> namespace //unnamed namespace { int count = 1; } using namespace std; namespace //unnamed namespace { void name_printf(void) { cout << "count = " << count << endl; } } int main(void) { count = 3; //直接使用 name_printf(); //直接使用 return 0; } 復制代碼 未命名的命名空間中定義的名字只在包含該命名空間的文件中可見。 如果另一文件包含一個未命名的命名空間,兩個命名空間不相關,可以定義相同的名字,而這些定義將引用不同的實體。 未命名的命名空間中成員的名字不能與全局作用域中定義的名字相同。例子如下,函數也是同樣的道理。 復制代碼 int i; // global variable namespace //unnamed namespace { int i; } // error: reference to ‘i’ is ambiguous 復制代碼 像其他命名空間一樣,未命名的命名空間也可以嵌套在另一命名空間內部。 如果未命名的命名空間是嵌套的,其中的名字按常規方法使用外圍命名空間名字訪問: 復制代碼 int i; //Global Variable namespace local { namespace //unnamed namespace { int i; // ok: i defined in a nested unnamed namespace is distinct from global i } } local::i = 42; 復制代碼 如果頭文件定義了未命名的命名空間,那麼,在每個包含該頭文件的文件中,該命名空間中的名字將定義不同的局部實體。 未命名的命名空間取代文件中的靜態聲明 在標准 C++ 中引入命名空間之前,程序必須將名字聲明為static,使它們的作用域約束於一個文件中。 文件中靜態聲明的方法是從 C 語言繼承而來, C++ 不贊成文件靜態聲明,因為這可能在未來版本中不支持。 匿名名字空間提供了類似在全局函數前加 static 修飾帶來的限制作用域的功能。 它的這種特性可以被用在struct和class上, 而static卻不能不能修飾class和struct這樣的結構定義。 所以應該避免文件靜態而使用未命名空間代替。 6、名字空間的別名 可以給名字空間起一個別名,別名是已定義的名字空間的可替換的名稱。 一個命名空間可以有許多別名,所有別名以及原來的命名空間名字都可以互換使用。 通過下面的形式將別名指定給已定義的名字空間的名字,就可以創建一個名字空間的別名。 namespace 別名 = 已定義的名字空間名字; 下面舉例說明名字空間別名的定義和使用 復制代碼 #include <iostream> namespace MyNames { int iVal1 = 100; int iVal2 = 200; } namespace MyAlias = MyNames; //別名定義 int main(void) { std::cout<<MyAlias::iVal1<<std::endl; //別名使用 std::cout<<MyAlias::iVal2<<std::endl; //別名使用 return 0; } 復制代碼 7、using聲明 和 using指示 使用using聲明 和 using指示 的好處就是可以使用使用名字空間中成員時,不必加上名字空間的作用域。 using std::cout; //using聲明 using namespace std; //using指示 7.1、using聲明(using declaration) 一個 using 聲明一次只引入一個命名空間成員。 using 聲明的作用域從 using 聲明點開始,直到包含 using 聲明的作用域的末尾,名字都是可見的。外部作用域中定義的同名實體被屏蔽。 using 聲明可以出現在全局、局部、類的作用域 和 名字空間中。在類作用域中using聲明只能引用基類成員。 復制代碼 //using declaration in Global Scope #include <iostream> using std::cout; //using聲明 using std::endl; //using聲明 int main(void) { cout<<"Hello World"<<endl; return 0; } 復制代碼 復制代碼 //using declaration in Local Scope #include <iostream> void func(void) { using std::cout; using std::endl; cout << "Using Declarations In Function"<<endl; } int main() { func(); // cout << "Hello" <<endl; //error: ‘cout’ and ‘endl’ were not declared in this scope return 0; } 復制代碼 復制代碼 //using declaration in Class Scope #include <stdio.h> class B { public: void f(void) {printf("In B::f()\n");} void g(void) {printf("In B::g()\n");} }; class C { public: void g() {printf("In C::g()\n");}; }; class D : public B { public: using B::f; // OK: B is a base of D2 using B::g; // OK: B is a base of D2 // using C::g; // error: C isn't a base of D2 }; int main(void) { D MyD; MyD.f(); MyD.g(); } 復制代碼 復制代碼 //using declaration in Namespce #include <iostream> namespace MyNames { using std::string; using std::cout; using std::endl; string str; void func(void){cout << "Hello"<<endl;} } int main(void) { MyNames::func(); return 0; } 復制代碼 7.2、using指示(using directive) using 指示使得整個名字空間中的成員都可見。 using 指示可以出現在全局、局部的作用域 和 名字空間中,不會出現在類的作用域中。 復制代碼 //using directive in Global Scope #include <iostream> using namespace std; //using指示 int main(void) { cout<<"Hello World"<<endl; return 0; } 復制代碼 復制代碼 //using directive in Local Scope #include <iostream> void func(void) { using namespace std; cout << "Using Declarations In Function"<<endl; } int main() { func(); // cout << "Hello" <<endl; //error: ‘cout’ and ‘endl’ were not declared in this scope return 0; } 復制代碼 復制代碼 //using declaration in Namespce #include <iostream> namespace MyNames { using namespace std; string str; void func(void){cout << "Hello"<<endl;} } int main(void) { MyNames::func(); // cout<<"Hello"<<endl; //error: ‘cout’ and ‘endl’ were not declared in this scope return 0; } 復制代碼 7.3、避免使用using指示 using 指示注入來自一個命名空間的所有名字,這個方法看似簡單,但是如果應用程序使用許多庫,並且用 using 指示使得這些庫中的名字可見,那麼,全局命名空間污染問題就重新出現。 相對於依賴於 using 指示,對程序中使用的每個命名空間名字使用using 聲明更好,這樣做減少注入到命名空間中的名字數目,由 using 聲明引起的二義性錯誤容易發現和修正。 8、綜合應用舉例 復制代碼 ////file : mynames.hpp #ifndef MYNAMES__HPP_ #define MYNAMES__HPP_ namespace MyNames { //Member:Variable extern int iVal; //Member:Class class MyString { public: MyString(const std::string&); void OutputString(void); private: std::string str; }; //Member:Function void NormalFunc(void); //Member:Struct struct st_Names { char ch; int count; }; //Member:Union union un_Names { char ch; int count; }; //Member:enum enum en_Names { ZERO, ONE, TWO }; } #endif /* MYNAMES__HPP_ */ //------------------------------------------------------------------------------------------------------------ //file : mynames.cpp #include <iostream> #include "mynames.hpp" namespace MyNames { int iVal = 100; MyString::MyString(const std::string& refstr) { str = refstr; } void MyString::OutputString(void) { std::cout << str << std::endl; } void NormalFunc(void) { std::cout << "NormalFunc" << std::endl; } } //------------------------------------------------------------------------------------------------------------- //file : example.cpp #include <iostream> #include "mynames.hpp" namespace Name = MyNames; using namespace Name; int main(void) { std::cout<<iVal<<std::endl; std::cout<<Name::iVal<<std::endl; std::cout<<MyNames::iVal<<std::endl; MyNames::MyString mystr("Hello"); mystr.OutputString(); MyNames::NormalFunc(); MyNames::st_Names myst; myst.count = 0; MyNames::en_Names myen; myen = MyNames::ZERO; MyNames::un_Names myun; myun.count = 1; return 0; }