目录
为什么要使用命名空间
一个大型的工程往往是由若干个人独立完成的,不同的人分别完成不同的部分,最后再组合成一个完整的程序。由于各个头文件是由不同的人设计的,有可能在不同的头文件中用了相同的名字来命名所定义的类或函数,这样在程序中就会出现名字冲突。不仅如此,有可能我们自己定义的名字会与C++库中的名字发生冲突。
名字冲突就是在同一个作用域中有两个或多个同名的实体,为了解决命名冲突 ,C++中引入了命名空间,所谓命名空间就是一个可以由用户自己定义的作用域,在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区分它们。
什么是命名空间?
命名空间又称为名字空间,是程序员命名的内存区域,程序员根据需要指定一些有名字的空间域,把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开。通俗的说,每个名字空间都是一个名字空间域,存放在名字空间域中的全局实体只在本空间域内有效。名字空间对全局实体加以域的限制,从而合理的解决命名冲突。
C++中定义命名空间的基本格式如下:
namespace wd { int val1 = 0; char val2; }// end of namespace wd
在声明一个命名空间时,大括号内不仅可以存放变量,还可以存放以下类型:
变量,常量,函数(可以是定义或声明),结构体,类,模板,命名空间(可以嵌套定义)namespace wd { int number = 0; struct Foo { char ch; int val; }; void display(); }// end of namespace wd
定义在命名空间中的变量或者函数都称为实体,名称空间中的实体作用域是全局的, 并不意味着其可见域是全局的。
如果不使用作用域限定符和using机制,抛开名称空间嵌套和内部屏蔽的情况,实体的可见域是从实体创建到该名称空间结束。
在命名空间外,该实体是不可见的。
命名空间的使用方式
名空间一共有三种使用方式,分别是using编译指令、作用域限定符、using声明机制。
using编译指令
我们接触的第一个C++程序基本上都是这样的,其中std代表的是标准命名空间。
#include <iostream> using namespace std; int main(int argc, char *argv[]) { cout << "hell,world" << endl; return 0; }
其中第二行就使用了using编译指令。如果一个名称空间中有多个实体,使用using编译指令,就会把该空间中的所有实体一次性引入到程序之中;对于初学者来说,如果对一个命名空间中的实体并不熟悉时,直接使用这种方式,有可能还是会造成名字冲突的问题,而且出现错误之后,还不好查找错误的原因,比如下面的程序就会报错,当然该错误是人为造成的。
#include <iostream> using namespace std; double cout() { return 1.1; } int main(void) { cout(); return 0; }
作用域限定符
第二种方式就是直接使用作用域限定符::啦。每次要使用某个名称空间中的实体时,都直接加上,例如:
namespace wd { int number = 10; void display() { //cout,endl都是std空间中的实体,所以都加上'std::'命名空间 std::cout << "wd::display()" << std::endl; } }//end of namespace wd int main(void) { std::cout << "wd::number = " << wd::number << endl; wd::display(); }
这种方式会显得比较冗余,所以还可以采用第三种使用方式。
using声明机制
using声明机制的作用域是从using语句开始,到using所在的作用域结束。要注意,在同一作用域内用using声明的不同的命名空间的成员不能有同名的成员,否则会发生重定义。
#include <iostream> using std::cout; using std::endl; namespace wd { int number = 10; void display() { cout << "wd::display()" << endl; } }//end of namespace wd using wd::number; using wd::display; int main(void) { cout << "wd::number = " << number << endl; wd::display(); }
在这三种方式之中,推荐使用的就是第三种,需要哪个实体的时候就引入到程序中,不需要的实体就不引入,尽可能减小犯错误的概率。
命名空间的嵌套及覆盖
int number = 1;
namespace wd
{
int number = 10;
namespace wh
{
int number = 100;
void display()
{
cout << "wd::wh::display()" << endl;
}
}//end of namespace wh
void display(int number)
{
cout << "形参number = " << number << endl;
cout << "wd命名空间中的number = " << wd::number << endl;
cout << "wh命名空间中的number = " << wd::wh::number << endl;
}
}//end of namespace wd
int main(void)
{
using wd::display;
display();
return 0;
}
对命名空间的思考和总结
下面引用当前流行的名称空间使用指导原则:
提倡在已命名的名称空间中定义变量,而不是直接定义外部全局变量或者静态全局变量。
如果开发了一个函数库或者类库,提倡将其放在一个名称空间中。
对于using 声明,首先将其作用域设置为局部而不是全局
不要在头文件中使用using编译指令,这样,使得可用名称变得模糊,容易出现二义性,
包含头文件的顺序可能会影响程序的行为,如果非要使用using编译指令,建议放在所有#include预编译指令后。