一、C++出世
我们先简单认识下C++的来历,C++是在C语言的基础上发展来的。
当年C++的设计者Bjarne Stroustrup,本贾尼·斯特劳斯特卢普先生设计C语言之初,是为了对C语言做出一些更改,弥补C语言在一些方面的不足,或者做出其他的设计,来让C语言更加适合程序员的使用。
简而言之,Bjarne Stroustrup当时只是考虑做些小的补充,然而后来,随着补充工作的逐渐增多,以及一些大的改动,他的“补充版C语言”最终演变成了一门新的语言,也就是C++.
二、命名空间的由来
我们现在知道,C++一开始是为了弥补C语言的一些不足和不方便之处。这篇文章介绍的命名空间,就是这一目的的产物。我们看下面这段C语言代码:
#include <stdio.h>
int rand = 0;
int main()
{
printf("%d\n", rand);
return 0;
}
它目前看起来没有任何问题。但如果我们在开头再引用上一个头文件<stdlib.h>,会怎么样呢?
我们很遗憾的看到,程序报了一个错误 ,告诉我们rand重定义了。这个报错的原因在于rand是stdlib.h中的一个库函数,程序编译时头文件展开,里面早已包含了一个对rand的定义。如果想要解决这个问题,我们通常能采取的办法就是修改自己的变量名,毕竟我们很难说能去修改库中的定义。
由此我们可以看出,在C语言中,我们取变量名时是受到一定的限制的,不能和库中的变量名相同。然而库函数成百上千,我们怎么能时时刻刻保证自己会避免踩坑呢?所以C++中就产生了命名空间这一概念。
我们创建一个命名空间域,把自己创建的rand变量放入这个命名空间域中,就可以避免上面的报错了:
namespace是C++中一个关键字,用于创建命名空间,后面自己指定的变量名me通常被称作域名。::是域作用限定符,通过域作用限定符,我们就可以找到命名空间中的变量并加以引用。
命名空间域中不仅可以创建变量名,也可以创建函数名,结构体共用体等等。
三、命名空间域的展开
1、展开某个命名空间域
从上面的例子可以看到,每一次使用命名空间域中的变量,都需要使用域作用限定符来指定一下在哪个命名空间中查找这个变量。如果对一个命名空间域中的变量使用次数很多时,这样的引用未免显得太过复杂,有没有什么办法可以不用再每次使用域作用限定符来指定,而是让编译器自动就去这个命名空间域中去找呢?答案是可以的:
#include <stdio.h>
namespace me
{
int a = 3;
}
using namespace me; //展开命名空间
int main()
{
printf("%d\n", a);
return 0;
}
通过展开命名空间,我们就可以省去域作用限定符,直接引用一个命名空间域中的变量。
2、展开命名空间域中某个变量
我们不仅可以展开一整个命名空间域,还可以精准的只展开某一命名空间域中的一个变量:
#include <stdio.h>
namespace me
{
int a = 3;
int b = 2;
}
using me::b;
int main()
{
printf("%d\n", b);
return 0;
}
四、变量的查找顺序
我们再看下面这个例子:
可以看到,单独使用rand变量时,编译器会首先使用局部域中的变量,其次在全局域中找,最后才会在命名空间域中找。::前没有指定命名空间域时,默认使用的是全局域。我们可以总结出以下规律:
当编译器引用变量时,引用顺序为局部域>全局域=展开的命名空间域>命名空间域(需声明)。
五、命名空间的一些规则
1、命名空间允许嵌套
命名空间是可以嵌套使用的:
#include <stdio.h>
namespace aaa
{
namespace mmm
{
int n = 0;
}
}
int main()
{
printf("%d\n", aaa::mmm::n);
return 0;
}
命名空间可以像这样一层套一层的连续使用,引用的时候依次使用域作用限定符链接即可。
2、同名的命名空间会相互合并
命名空间是可以重名的,重名的命名空间会在编译时相互合并,视为一个命名空间。
同一文件中的重名命名空间会合并,不同文件中的重名命名空间也会合并。