在C++中,名称可以是变量、函数、结构、枚举、类以及类和结构的成员。当随着项目的增大,名称相互冲突的可能性也将增加。使用多个厂商的类库时,可能导致名称冲突。例如,两个库可能都定义了名为List、Tree和Node的类 ,但定义的方式不兼容。用户可能希望使用一一个库的List类,而使用另一一个库的Tree类。这种冲突被称为名称空间问题。
C++标准提供了名称空间工具,以便更好地控制名称的作用域。经过了一段时间后,编译器才支持名称空间,但现在这种支持很普遍。内联函数不受这项规则的约束,这允许程序员能够将内联函数的定义放在头文件中。这样,包含了头文件的每个文件都有内联函数的定义。然而,C++要求同一个函数的所有内联定义都必须相同。
- 第一个需要知道的术语是声明区域( declaration region)。声明区域是可以在其中进行声明的区域。例如,可以在函数外面声明全局变量,对于这种变量 , 其声明区域为其声明所在的文件。对于在函数中声明的变量,其声明区域为其声明所在的代码块。
- 第二个需要知道的术语是潜在作用域( potential scope )。变量的潜在作用域从声明点开始,到其声明区域的结尾。因此潜在作用域比声明区域小,这是由于变量必须定义后才能使用。
1.新的名称空间特性
C++新增了这样一种功能,即通过定义一种新的声明区域来创建命名的名称空间,这样做的目的之一是提供一个声明名称的区域。一个名称空间中的名称不会与另外一个名称空间的相同名称发生冲突,同时允许程序的其他部分使用该名称空间中声明的东西。例如,下面的代码使用新的关键字namespace创建了两个名称空间: Jack和Jill。
1.1using声明和using编译指令
我们并不希望每次使用名称时都对它进行限定,因此C++提供了两种机制( using声明和using编译指
令)来简化对名称空间中名称的使用。using声明使特定的标识符可用 , using编译指令使整个名称空间可用。
using声明将特定的名称添加到它所属的声明区域中。例如main()中的using声Jill::fetch将fetch添加到main()定义的声明区域中。完成该声明后,便可以使用名称fetch代替jill:fetch。下面的代码段说明了这几点:
using声明使一个名称可用,而using编译指令使所有的名称都可用。using编译指令由名称空间名和它前面的关键字using namespace组成,它使名称空间中的所有名称都可用,而不需要使用作用域解析运算符:
1.2using编译指令和using声明之比较
使用using编译指令导入一个名称空间中所有的名称与使用多个using声明是不一样的,而更像是大量使用作用域解析运算符。使用using声明时 ,就好像声明了相应的名称一样。如果某个名称已经在函数中声明了,则不能用using声明导入相同的名称。然而,使用using编译指令时,将进行名称解析,就像在包含using声明和名称空间本身的最小声明区域中声明了名称一样。在下面的示例中,名称空间为全局的。如果使用using编译指令导入一个已经在函数中声明的名称,则局部名称将隐藏名称空间名,就像隐藏同名的全局变量一样。不过仍可以像下面的示例中那样使用作用域解析运算符:
在main()中,名称Jill:fetch被放在局部名称空间中,但其作用域不是局部的,因此不会覆盖全局fetch。然而,局部声明的fetch将隐藏Jill:fetch和全局fetch。然而,如果使用作用域解析运算符,则后两个fetch变量都是可用的。
应将这个示例与前面使用using声明的示例进行比较。需要指出的另一点是,虽然函数中的using编译指令将名称空间的名称视为在函数之外声明的,但它不会使得该文件中的其他函数能够使用这些名称。因此,在前一个例子中,foom()函数不能使用未限定的标识符Hill。
注意:假设名称空间和声明区域定义了相同的名称。如果试图使用using声明将名称空间的名称导入该声明区域,则这两个名称会发生冲突,从而出错。如果使用using编译指令将该名称空间的名称导入该声明区域,则局部版本将隐藏名称空间版本。
1.3.名称空间的其他特性
这里, flame指的是elemet:fire::flame。同样,可以使用下面的using编译指令使内部的名称可用:
1.4.未命名的名称空间
这就像后面跟着using编译指令一样,也就是说,在该名称空间中声明的名称的潜在作用域为:从声明点到该声明区域末尾。从这个方面看,它们与全局变量相似。然而,由于这种名称空间没有名称,因此不能显式地使用using编译指令或using声明来使它在其他位置都可用。具体地说,不能在未命名名称空间所属文件之外的其他文件中,使用该名称空间中的名称。这提供了链接性为内部的静态变量的替代品。例如,假设有这样的代码: