前言
当随着C++项目项目的增大,名称相互冲突的可能性也将增加。使用多个厂商的类库时,可能导致名称冲突。例如,两个类库可能都定义了名为List, Tree和Node的类,但定义的方式不兼容。用户希望使用其中一个库的List类,而使用另一个库的Tree类。这种冲突就被称为名称空间问题。
A demo for namespace
namespace Jack {
double pail; // 变量声明
void fetch; // 函数原型
int pal; // 变量声明
struct Well { ... }; // 结构体声明
}
创建名称空间的关键字是namespace,但是需要注意:
名称空间可以是全局的,也可以位于另一个名称空间中(名称空间的嵌套),但是不能位于代码块中。因此,在默认情况下,在名称空间中声明的名称的链接性为外部的。
除了用户自己定义的名称空间外,还存在一个全局名称空间(global namespace)。它对应于文件级声明区域,因此前面所说的全局变量现在被描述为位于全局名称空间中。
任何名称空间中的名称都不会与其它名称空间中的名称发生冲突。
-
名称空间是开放的(open),即可以把名称加入到已有的名称空间中。例如,下面的代码可以将名称goose加入到Jack已有的名称列表中:
namespace Jack { char * goose(const char *); }
通过作用域解析运算符“::”可以访问给定的名称空间,如
Jack::pail = 12.34 //使用一个变量
Jack::Hill mole; //使用一个结构体
Jack::fetch(); //使用一个函数
using声明和using编译指令
对于名称空间内的成员,我们在编写程序的时候不希望每次使用的时候都显示地对它们的名称空间加以限定。对于那些经常用到的部分,如std::cout,如果总是这种写法的话,将不得已增加程序代码的数量。对此,C++提供了两种机制来简化对名称空间的使用,它们分别是using声明和using编译指令。
using声明由被限定的名称和它前面的关键字using组成:
using Jack::fetch; // 一个using声明
这样,以后每次再使用fetch,都可以省去其名称空间。
namespace Jill {
double bucket(double n) { ... };
double fetch;
struct Hill { ... };
}
char fetch;
int main() {
using Jill::fetch; // 把fetch加到local的名称空间中
double fetch; // 编译错误!因为我们已经有一个local的fetch了
cin >> fetch; // 输入Jill::fetch
cin >> ::fetch; // 输入全局的fetch
}
将using添加到局部变量的声明区域中,避免了将另一个局部变量也命名为fetch。此外,和其它局部变量一样,fetch也将覆盖同名的全局变量。
namespace Jill {
double bucket(double n) { ... };
double fetch;
struct Hill { ... };
}
using Jill::fetch; // 将fetch加入到全局的名称空间中
int main() {
cin >> fetch; // 输入Jill::fetch
cout << fetch; // 输出Jill::fetch
}
在全局声明区域中使用using编译指令,将使该名称空间的名称全局可用,这里也包括我们非常熟悉的
#include<iostream>
using namespace std;
简单地说,using声明使得一个名称可用,而using编译指令使所有的名称都可用。using编译指令由名称空间名和它前面的关键字using namespace组成,它使名称空间中的所有名称都可用,而不需要使用作用域解析运算符。
一般来说,使用using声明比使用using编译指令更加安全,这是由于它只导入了特定的名称,如果发生与局部名称之间的命名冲突,编译器会通知我们。而如果使用using编译指令,将导入所有的名称,包括可能不需要的名称,如果与局部变量发生命名从图,局部名称很可能覆盖名称空间的版本。
名称空间的其它特性:
嵌套:
namespace elements
{
namespace fire
{
int flame;
...
}
float water;
}
flame指的是elements::fire::flame,若想使得其名称可用,可以使用如下声明:
using namespace elements::fire;
与此同时,也可以在名称空间中使用using编译指令和using声明,如下所示,
namespace myth
{
using Jill::fetch;
using namespace elements;
using std::cout;
using std::cin;
}
这种情况下,如果想访问Jill::fetch,可以使用myth::fetch,也可以使用Jill::fetch,二者等价。
对于using编译指令来说,嵌套所带来的影响是传递性。即如果A op B 且 B op C, 则 A op C。所以以下语句将导入的名称空间是myth和elements:
using namespace myth;
等价于:
using namespace myth;
using namespace elements;
未命名的名称空间:
可以通过省略名称空间的名称来创建未命名的名称空间:
namespace // unamed namespace
{
int ice;
int bandycoot;
}
这种定义方法与全局变量类似。然而,由于该名称空间没有名称,因此不能显式地使用using编译指令或using声明来使其在其它地方都可用。具体地说,不能在未命名名称空间所属文件之外的其他文件中,使用该名称空间中的名称。这提供了链接性为内部的静态变量的替代品,与static关键字的作用相同。因此以上代码等价于:
static int ice;
static bandycoot;
有关std:
如果开发了一个函数库或类库,将其放在一个名称空间中。事实上,C++提倡将标准函数库放在名称空间std中,这种做法扩展到了来自C语言中的函数。例如,头文件math.h是与C语言兼容的,没有使用名称空间,但C++头文件cmath应将各种数学库函数方在名称空间std中。实际上,并非所有的编译器都完成了这种过渡。正如前面指出的,老式头文件(如iostream.h)没有使用名称空间,但新头文件iostream使用了std名称空间。