C++编程中的命名空间基本知识讲解

C++编程中的命名空间基本知识讲解 命名空间是一个声明性区域为其内部的标识符类型、函数和变量等的名称提供一个范围。命名空间用于将代码组织到逻辑组中还可用于避免名称冲突尤其是在基本代码包括多个库时。命名空间范围内的所有标识符彼此可见而没有任何限制。命名空间之外的标识符可通过使用每个标识符的完全限定名例如 std::vectorstd::string vec;来访问成员也可通过单个标识符的 using 声明 (using std::string) 或命名空间中所有标识符的 using 指令 (C) (using namespace std;) 来访问成员。头文件中的代码应始终使用完全限定的命名空间名称。下面的示例演示了一个命名空间声明和命名空间之外的代码可访问其成员的三种方法。123456789namespaceContosoData{classObjectManager{public:voidDoSomething() {}};voidFunc(ObjectManager) {}}使用完全限定名123ContosoData::ObjectManager mgr;mgr.DoSomething();ContosoData::Func(mgr);使用 using 声明以将一个标识符引入范围123usingWidgetsUnlimited::ObjectManager;ObjectManager mgr;mgr.DoSomething();使用 using 指令以将命名空间中的所有内容引入范围1234usingnamespaceWidgetsUnlimited;ObjectManager mgr;mgr.DoSomething();Func(mgr);using 指令通过 using 指令可使用命名空间中的所有名称而不需要命名空间名称为显式限定符。如果在一个命名空间中使用多个不同的标识符则在实现文件中使用 using 指令即*.cpp如果仅使用一个或两个标识符则考虑使用声明以仅将这些标识符而不是命名空间中的所有标识符引入范围。如果本地变量的名称与命名空间变量的名称相同则隐藏命名空间变量。使命名空间变量具有与全局变量相同的名称是错误的。注意using 指令可以放置在 .cpp 文件的顶部在文件范围内或放置在类或函数定义内。一般情况下避免将 using 指令放置在头文件 (*.h) 中因为任何包含该标头的文件都会将命名空间中的所有内容引入范围这将导致非常难以调试的名称隐藏和名称冲突问题。在头文件中始终使用完全限定名。如果这些名称太长可以使用命名空间别名将其缩短。请参阅下文。声明命名空间和命名空间成员通常情况下在头文件中声明一个命名空间。如果函数实现位于一个单独的文件中则限定函数名称如本示例所示。12345678910111213141516171819//contosoData.h#pragma oncenamespaceContosoDataServer{voidFoo();intBar();}contosodata.cpp 中的函数实现应使用完全限定名即使将一个using指令放置于文件的顶部也是如此#include contosodata.husingnamespaceContosoDataServer;voidContosoDataServer::Foo(){//no qualification because using directive aboveBar();}intContosoDataServer::Bar(){return0;}可以在单个文件中的多个块中声明命名空间也可在多个文件中声明命名空间。编译器在预处理过程中将各部分联接在一起产生的命名空间中包含所有部分中声明的所有成员。一个相关示例是在标准库中的每个头文件中声明的 std 命名空间。指定的命名空间的成员可以在定义的名称的显式限定所声明的命名空间的外部进行定义。但是定义必须出现在命名空间中的声明位置之后该命名空间包含在声明的命名空间中。例如:12345678910111213// defining_namespace_members.cpp// C2039 expectednamespaceV {voidf();}voidV::f() { }// okvoidV::g() { }// C2039, g() is not yet a member of VnamespaceV {voidg();}}当跨多个头文件声明命名空间成员并且未以正确的顺序包含这些标头时可能出现此错误。全局命名空间如果未在显式命名空间中声明某个标识符则该标识符属于隐式全局命名空间的一部分。通常情况下如果可能尝试避免在全局范围内进行声明入口点 main 函数除外它必须位于全局命名空间中。若要显式限定全局标识符请使用没有名称的范围解析运算符如 ::SomeFunction(x); 中所示。这将使标识符与任何其他命名空间中具有相同名称的任何内容区分开来并且还有助于使其他人更轻松地了解你的代码。Std 命名空间所有 C 标准库类型和函数都在 std 命名空间或嵌套在 std 内的命名空间中进行声明。嵌套命名空间可以嵌套命名空间。普通的嵌套命名空间具有对其父级成员的非限定访问权限而父成员不具有对嵌套命名空间的非限定访问权限除非它被声明为内联如下面的示例所示1234567891011121314namespaceContosoDataServer{voidFoo();namespaceDetails{intCountImpl;voidBan() {returnFoo(); }}intBar(){...};intBaz(inti) {returnDetails::CountImpl; }}普通嵌套命名空间可用于封装不属于父命名空间的公共接口的一部分的内部实现详细信息。内联命名空间 (C 11)与普通嵌套命名空间不同内联命名空间的成员会被视为父命名空间的成员。这一特性使针对重载函数的依赖于参数的查找可以对父命名空间和嵌套内联命名空间中具有重载的函数起作用。它还可让你在内联命名空间中声明的模板的父命名空间中声明专用化。下面的示例演示在默认情况下外部代码如何绑定到内联命名空间123456789101112131415161718192021222324252627282930//Header.h#include stringnamespaceTest{namespaceold_ns{std::string Func() {returnstd::string(Hello from old); }}inlinenamespacenew_ns{std::string Func() {returnstd::string(Hello from new); }}}#include header.h#include string#include iostreamintmain(){usingnamespaceTest;usingnamespacestd;string s Func();std::cout s std::endl;// Hello from newreturn0;}下面的示例演示如何在内联命名空间中声明的模板的父命名空间中声明专用化12345678910111213namespaceParent{inlinenamespacenew_ns{templatetypenameTstructC{T member;};}templateclassCint {};}可以将内联命名空间用作版本控制机制以管理对库的公共接口的更改。例如可以创建单个父命名空间并将接口的每个版本封装到嵌套在父命名空间内的其自己的命名空间中。保留最新或首选的版本的命名空间限定为内联并因此以父命名空间的直接成员的形式公开。调用 Parent::Class 的客户端代码将自动绑定到新代码。通过使用指向包含该代码的嵌套命名空间的完全限定路径选择使用较旧版本的客户端仍可以对其进行访问。Inline 关键字必须应用到编译单元中命名空间的第一个声明中。下面的示例演示一个接口的两个版本每个版本位于一个嵌套命名空间中。通过 v_10 接口对 v_20 命名空间进行了某些修改且该命名空间被标记为内联。使用新库并调用 Contoso::Funcs::Add 的客户端代码将调用 v_20 版本。尝试调用 Contoso::Funcs::Divide 的代码现在将获取一个编译时错误。如果它们确实需要该函数则仍可以通过显式调用 Contoso::v_10::Funcs::Divide 访问 v_10 版本。1234567891011121314151617181920212223242526272829303132namespaceContoso{namespacev_10{templatetypenameTclassFuncs{public:Funcs(void);T Add(T a, T b);T Subtract(T a, T b);T Multiply(T a, T b);T Divide(T a, T b);};}inlinenamespacev_20{templatetypenameTclassFuncs{public:Funcs(void);T Add(T a, T b);T Subtract(T a, T b);T Multiply(T a, T b);std::vectordouble Log(double);T Accumulate(std::vectorT nums);};}}命名空间别名命名空间名称必须是唯一的这意味着通常它们不应太短。如果名称的长度使代码难以阅读或在不能使用 using 指令的头文件中进行键入单调乏味则可以使用用作实际名称的缩写的命名空间别名。