C++名字空间/C++命名空间
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++ 不赞成文件静态声明,因为这可能在未来版本中不支持。
所以应该避免文件静态而使用未命名空间代替。
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; }
-------------------------------------------------------------------------------------------------------------------------------------------------------------
参考资料:
http://blog.csdn.net/syhhl007/article/details/4678786
<<C++ Primer 4th>>
http://msdn.microsoft.com/en-us/library/was37tzw.aspx
http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-inline-namespace.html