1、全局作用域日益变得拥挤不堪。
例如,许多库都希望包括一个名为String的类。但是,如果使用两个不同的库,其中分别定义了一个string类型,就会产生多重定义错误甚至更糟。
2、本质上,名字空间是对全局作用域的细分:
namespace org_semantics {
class String { .... };
String operator + ( const String &, const String & );
//其他类、函数、类型定义等...
}
这个代码片段打开了一个名为org_semantics的名字空间,声明了一些有用的东西,然后采用闭花括号关闭该名字空间。总是可以向一个名字空间加入更多的东西,只要重复上述过程。
名字空间对所有人都是“开放”的。
可以注意到名字空间org_semantics中有些名字只有声明而没有定义。为了给这些名字提供定义,可以重新打开该名字空间:
namespace org_semantics {
String operator + ( const String &a, String &b ) { //...... }
}
3、还有一种选择,就是可以仅仅为该定义加上名字空间限定而无需重新打开名字空间:
org_semantics::String
org_semantics::operator + ( const org_semantics::String &a, const org_aemantics::String &b )
{ //..... }
这种方式可以防止一不小心声明了一个新的名字空间(正如我们在第一个operator+定义中遗漏了对第二个参数进行const修饰那样),而不是定义了一个已经声明的空间。
4、如果希望使用定义于一个特有名字空间中的名字,必须告诉编译器该名字可以在哪一个名字空间中找到:
org_semantics::String s ( " Hello, World!" );
尽管一些C++标准库组件还呆在全局作用域中(这些组件包括全局operator new、operator delete、array new以及array delete等),然而大多数标准库组件现驻留在std(意指standard标准)名字空间中,因而对大多数标准库组件的使用,需要限定std名称:
#include<iostream>
#include<vector>
//....
void aFunc ( ) {
vector<int> a; //错误,没看到哪里定义了vector
std::vector<int > b; //ok
cout<<" opo" <<endl; //错误
std::cout<< "Better!"<<std::endl; //ok
//....
}
显然,连续使用显式的限定符很乏味。缓解方式是使用using指令:
void aFunc( ) {
using namespace std; //using 指令
vector<int > a; //ok
cout<<"Hello"<<endl; //ok
//...
}
从本质上讲,using指令从名字空间中导入名字,使它们在该using指令的作用域内无需进行限定就可以被访问。
在本例中,using指令的作用域一直延伸到函数体末尾,后面再想使用该名字空间的东西,还需进行明确的限定。
正因为因为如此,许多C++程序员建议将using指令放在全局作用域中:
#include<iostream>
#include<vector>
using namespace std;
using namespace org_sematics;
这是个馊主意!
如此一来,名字空间中的所有名字在任何地方都可以被访问了,从而可能导致混淆。
在头文件中这么做更糟糕,因为所有包含该头文件的文件都会受到影响。
在头文件中,我们通常坚持使用显式的限定,并且仅将using指令局限于较小的作用域中(例如函数体或函数体内的某个代码块),这样它们的效用就会受到限制并易于控制。
基本来说,在头文件中要坚持表现得最好,在源文件中要表现的足够好,而在函数体内大可放松一下。
5、使用using指令的一个有趣的方面在于,它使一个名字空间中的名字变得可用,但这种“可用”又不能算是绝对的可用,其实际效果相当于该名字空间中的名字被声明在全局作用域中,而非局限于using指令所在的作用域中。
所以说,如果using指令作用域中出现同名名字,就会隐藏该名字空间中的相应名字:
void aFunc( ) {
using namespace std; //using 指令
//....
int vector = 12; //一个欠佳的具名局部变量
vector<int > a; //错误!std::vector被隐藏了
std::vector< int> b; //ok,可以使用显式的限定
//....
}
6、另一种方法是使用using声明,它通过一个真正的声明提供对名字空间中名字的访问:
void aFunc( ) {
using std::vector; //using声明
//....
int vector = 12; //错误!重新声明vector
vector<int > a; //ok
//....
}
using声明通常是介于冗长乏味的显式限定和不受限制地使用using指令之间的一种折中。
7、另一种方式是使用别名:
namespace S = org_semantics;
现在S可以用于代替org_semantics(在别名的作用域内)。与using指令一样,最好避免在头文件中使用名字空间别名(毕竟S远比org_sematics更容易与其他名字冲突)。