在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(命名空间),可以更好地控制标识符的作用域。(MFC中并没有使用命名空间,但是在.NET框架、MC++和C++/CLI中,都大量使用了命名空间。)
C语言标识符有局部(代码块{…},如复合语句和函数体)和全局两种作用域,C++在这二者之间引入了类作用域(如类变量和成员函数)。标准C++又在类和全局之间,新添加了命名空间这一个作用域级别。
命名空间是一种描述逻辑分组的机制,可以将按某些标准在逻辑上属于同一个集团的声明放在同一个命名空间中。命名空间可以是全局的,也可以位于另一个命名空间之中,但是不能位于类和代码块中。所以,在命名空间中声明的名称(标识符),默认具有外部链接特性(除非它引用了常量)。在所有命名空间之外,还存在一个全局命名空间,它对应于文件级的声明域。因此,在命名空间机制中,原来的全局变量,现在被认为位于全局命名空间中。
标准C++库(不包括标准C库)中所包含的所有内容(包括常量、变量、结构、类和函数等)都被定义在命名空间std(standard,标准)中了。
1)定义命名空间
有两种形式的命名空间——有名的和无名的,它们的定义方法分别为:
namespace 命名空间名 { // 有名命名空间
[声明序列]
}
namespace { // 无名命名空间
[声明序列]
}
命名空间的成员,是在命名空间定义中的花括号内声明了的名称。可以在命名空间的定义内,定义命名空间的成员(内部定义)。也可以只在命名空间的定义内声明成员,而在命名空间的定义之外,定义命名空间的成员(外部定义)。
命名空间成员的外部定义的格式为:
命名空间名::成员名 ……
注意:不能在命名空间的定义中声明(另一个嵌套的)子命名空间,只能在命名空间的定义中定义子命名空间。也不能直接使用“命名空间名::成员名 ……”定义方式,为命名空间添加新成员,而必须先在命名空间的定义中添加新成员的声明。另外,命名空间是开放的,即可以随时把新的成员名称加入到已有的命名空间之中去。方法是,多次声明和定义同一命名空间,每次添加自己的新成员和名称。例如:
namespace A { int i; void f(); } // 现在A有成员i和f()
namespace A { int j; void g(); } // 现在A有成员i、f()、j和g()
2)使用命名空间
使用命名空间的方法有三种:
A、作用域解析运算符(::)
对命名空间中成员的引用,需要使用命名空间的作用域解析运算符::。例如:
std::cout << "Hello, World!" << std::endl;
B、using指令(using namespace)
为了省去每次调用命名空间成员和标准库的函数和对象时,都要添加“命名空间名::”和“std::”的麻烦,可以使用标准C++的using编译指令来简化对命名空间中的名称的使用。格式为:
using namespace 命名空间名[::子命名空间名……];
在这条语句之后,就可以直接使用该命名空间中的标识符,而不必写前面的命名空间定位部分。因为using指令,使所指定的整个命名空间中的所有成员都直接可用。例如:
using namespace std;
cout << "Hello, World!" << endl;
又例如(.NET框架):
using namespace System::Drawing::Imaging;
using namespace System::Window::Forms::Design::Behavior;
C、using声明(using)
除了可以使用using编译指令(组合关键字using namespace)外,还可以使用using声明来简化对命名空间中的名称的使用。格式为:
using 命名空间名::[命名空间名::……]成员名;
注意,关键字using后面并没有跟关键字namespace,而且最后必须为命名空间的成员名(而在using编译指令的最后,必须为命名空间名)。
与using指令不同的是,using声明只是把命名空间的特定成员的名称,添加该声明所在的区域中,使得该成员可以不需要采用,(多级)命名空间的作用域解析运算符来定位,而直接被使用。但是该命名空间的其他成员,仍然需要作用域解析运算符来定位。例如:
using std::cout;
cout << "Hello, World!" << std::endl;
D、using指令与using声明的比较
可见,using编译指令和using声明,都可以简化对命名空间中名称的访问。
using指令使用后,可以一劳永逸,对整个命名空间的所有成员都有效,非常方便。而using声明,则必须对命名空间的不同成员名称,一个一个地去声明,非常麻烦。
但是,一般来说,使用using声明会更安全。因为,using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器会报错。而using指令导入整个命名空间中的所有成员的名称,包括那些可能根本用不到的名称,如果其中有名称与局部名称发生冲突,则编译器并不会发出任何警告信息,而只是用局部名去自动覆盖命名空间中的同名成员。特别是命名空间的开放性,使得一个命名空间的成员,可能分散在多个地方,程序员难以准确知道,别人到底为该命名空间添加了哪些名称。
虽然使用命名空间的方法,有多种可供选择。但是不能贪图方便,一味使用using 指令,这样就完全背离了设计命名空间的初衷,也失去了命名空间应该具有的防止名称冲突的功能。
一般情况下,对偶尔使用的命名空间成员,应该使用命名空间的作用域解析运算符来直接给名称定位。而对一个大命名空间中的经常要使用的少数几个成员,提倡使用using声明,而不应该使用using编译指令。只有需要反复使用同一个命名空间的许多数成员时,使用using编译指令,才被认为是可取的。
新闻热点
疑难解答