25. 命名空间

命名空间(namespace)为防止名字冲突提供了更加可控的机制。命名空间分割了全局命名空间,每个命名空间是一个作用域。通过在某个命名空间中定义库的名字,可以避免全局名字固有的限制。

25.1. 命名空间的定义

只要能出现在全局作用域中的声明都能置于命名空间内。

  • 变量(及其初始化)

  • 函数(及其定义)

  • 模板

  • 其他命名空间

1namespace cpp
2{
3  class A{ /* ... */};
4  A operator+(const A&, const A&);
5  class B: public A{ /* ... */};
6}// 这里没有分号

命名空间可以定义在全局作用域,也可以定义在其他命名空间中(嵌套命名空间,内层命名空间声明的名字将隐藏外层命名空间的名字),但是不能定义在函数或类的内部。

作用域

不同命名空间的作用域不同,因此不同命名空间内可以有相同名字的成员。

  • 定义在某个命名空间中的名字可以被该命名空间内的其他成员直接访问,也可以被这些成员内嵌作用域中的任何单位访问。

  • 位于命名空间之外的代码必须明确指出所用的名字属于哪个命名空间( :: )。

    cpp::B obj = cpp::B("hello");
    

不连续性

1namespace cpp
2{
3  /* ... */
4}

同一个命名空间可以定义在几个不同的地方。如果之前没有命名为 cpp 的命名空间,则上述代码创建一个新的命名空间;否则,上述代码打开已经存在的命名空间定义,并添加新的成员的声明。

命名空间的组织形式
  • 命名空间的一部分成员的作用是定义类,以及声明作为类接口的函数及对象,这些成员应该放在头文件中。使用命名空间成员的文件需要包含这个头文件。

  • 命名空间成员的定义部分放在另外的源文件中。

全局命名空间

全局作用域(即在所有类、函数、命名空间之外)中定义的名字就是定义在全局命名空间中。全局命名空间以隐式的方式声明,并在所有的程序中存在。

作用域运算符 :: 可以用于全局作用域的成员。由于全局作用域是隐式的,所以它并没有名字,

::global_var += 1;

内联命名空间

内联命名空间中的名字可以被外层命名空间直接使用而无需添加命名空间的前缀( :: )。

 1// FifthEd.h
 2inline namespace FifthEd
 3{
 4  int a = 1;
 5}
 6
 7// FourthEd.h
 8namespace FourthEd
 9{
10  int b = 1;
11}
12
13// cpp.h
14inline namespace cpp
15{
16  #include "FifthEd.h"
17  #include "FourthEd.h"
18  // 访问 a,cpp::a
19  // 访问 b,cpp::FourthEd::b
20}

未命名的命名空间

未命名的命名空间是由关键字 namespace 紧跟花括号括起来的一系列声明语句。

未命名的命名空间中定义的变量将拥有静态声明周期:它们在第一次使用前创建,并且直到程序结束时才销毁。

未命名的命名空间可以在某个给定的文件内不连续,但是不能跨越多个文件。每个文件定义自己的未命名的命名空间,如果两个文件都含有未命名的命名空间,则这两个空间互相无关。在这两个未命名的命名空间中可以定义相同的名字,并且这些定义表示不同的实体。

定义在未命名空间中的名字可以直接使用,不能对未命名空间的成员使用作用域运算符。

未命名的命名空间中定义的名字的作用域与该命名空间所在的作用域相同。如果未命名的命名空间定义在文件的最外层作用域中,则该命名空间中的名字一定要与全局作用域的名字有所区别,否则将会引起命名冲突。

Note

在标准 C++ 引入命名空间的概念之前,程序需要将名字声明成 static 以使得其在整个文件有效,在文件之外不可见。 在文件中进行静态声明的做法已经被 C++ 标准取消了,现在的做法是使用未命名的命名空间。

25.2. 使用命名空间

使用别名

namespace primer = cplusplus_primer;

using 声明

一条 using 声明(declaration)语句一次只引入命名空间的一个成员;

using std::cout;// cout 的调用不再需要作用域运算符,其他 std 命名空间中的名字仍然需要作用域运算符

using 声明可以出现在全局作用域、局部作用域、命名空间作用域、类的作用域中(只能指向基类成员)。

using 指示

using 指示(directive)语句使得该命名空间内的所有名字都可见。

using namespace std;

using 指示可以出现在全局作用域、局部作用域、命名空间作用域中,不能出现在类的作用域中。

例子:

 1namespace cpp
 2{
 3  int i = 16;
 4  int j = 15;
 5  int k = 23;
 6}
 7int j = 0;
 8void foo()
 9{
10  using namespace cpp;
11  ++ i; // cpp::i
12  ++ j; // 二义性
13  ++ ::j; // 全局作用域的 j
14  ++ cpp::j; // cpp::j
15  int k = 0; // 局部 k 隐藏了 cpp::k
16  ++ k; // 局部 k
17}

25.3. 参考资料

1.《C++ Primer 第5版 中文版》 Page 695 – 710。

  1. 命名空间