目录
命名空间
1.由来
2.概念
3.使用
方法一
方法二
方法三
4.嵌套使用
命名空间
很多人在学习C++的时候,第一个代码想必是如下样子的。和C语言相比,库函数换了,同时多了一个 using namespace std; 但是,你真的了解 using namespace std; 的真实作用吗?去掉 using namespace std; 会有什么影响呢?一定要加上这句话,才可以完整地打出一个Hello World 程序吗?有没有不加的办法?还是老师说要加上这句话,于是就加了。
如果对于前面的问题,你都理解了,那么很遗憾,本部分内容不是为你准备的,如果向了解缺省参数,倒是可以看一下。如果你并不知道,并且你也想了解它们,那么恭喜你,这一部分的内容就是为你而准备的!
#include<iostream>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
1.由来
我们知道,C++起初是Bjarne Stroustrup博士在C语言的基础上,不断加入新的特性产生的。那么C++肯定是为了弥补C语言的一些不足之处。那么命名空间是为了解决C语言的什么问题呢?
我们看到下面左右两边的程序,右边相比于左边上面的,多了一行包含 stdlib.h 的头文件,就报错了,并且错误信息也在下方——“rand” 重定义。很容易联想到,stdlib.h 库里面是有 rand 函数的,我们可以用来生成随机数,那么就是自定义的全局变量名,和库里面的变量名冲突了。但是,我们将这个 rand 变量放到 main 函数内部,让它成为局部变量,就没有这个问题。这是因为,stdlib.h 是在全局域里面的,如果定义全局变量 rand,那么该变量也在全局域,自然会发生冲突,但是如果相同的名字,一个在全局域一个在局部域,就不会冲突。又比如两个不同的函数里面,都定义了 int a; 变量,也是可以的。
与此同时,如果在一个项目组里面,组内成员分别负责不同板块的代码编写。如果组员A 和 组员B 都使用 Add 作为某个函数的函数名,或者是起了相同名字的变量(全局)等等,那么最后项目汇总起来,也会报错。
所以,C语言在这方面会产生两个主要的问题,一是自己定义和库里面的名字冲突;二是项目组里面,多个人之间的名字冲突。为了解决这个问题,博士就在C++里面加上了命名空间这一概念,命名空间是唯一识别的一套名字,这样当对象来自不同的地方但是名字相同的时候就不会含糊不清了。
2.概念
命名空间定义的实际上是一个域,这个域相当于在全局域的基础之上,又加了一层域,就是命名空间域。并且,命名空间只影响其内部的变量,函数等等的使用,不影响生命周期。上面讲到,同一个域里面不可以有同名变量,但是不同的域里面可以有。那么定义了命名空间,增加出来的命名空间域,就可以解决上面的问题。假设一个组里面有A、B两个人写代码,A将他的代码放到 A域 里面,B将他的代码放到 B域 里面,这也即使两个人定义了同名变量,也不影响。
命名空间使用规则并不难,需要使用到关键字 namespace。如下代码,namespace 加上 该命名空间的名字,再加上花括号即可。花括号里面可以写任意的变量或者函数名,这些内容就成了该命名空间里面的,相当于和外界隔绝,如果不声明一下要使用,编译器是无法找到的,如何声明下文会讲。但是一个命名空间里面自然也是不可以有同名变量,除非是在不同的函数里面,如下。
namespace name
{
int a;
double b;
char c;
int Add(int m, int n)
{
int a = m + n;
return a;
}
int Minus(int m, int n)
{
int a = m - n;
return a;
}
}
如下,左边两个是头文件,右边的是.cpp 文件,将两个头文件包含进去,写出Hello World 的代码,也报错,原因如下,struct Node 和 m 重定义。
但是我们把两个头文件,分别放到两个命名空间,就可以避免这个问题。
3.使用
上面讲到,如果不声明,编译器无法找到 命名空间里面的内容。那么如何做声明呢?有三种方法。
1.指定命名空间访问。
2.全局展开,一般情况下,不建议全局展开。
3.部分。
方法一
首先看到平时练习最常用的,全局展开。如下图,对于命名空间 AQueue ,直接使用using namespace AQueue; 就是全局展开,相当于告诉编译器:找变量名、函数等等,除了局部域和全局域,也可以去AQueue这个域找。
但是注意下图的代码,并没有全局展开头文件List.h 里面的 BList 这个命名空间,那么是不是代表BList 这个命名空间被取消了?并不是,命名空间还在那里,只不过是编译器寻找变量名等等不会去 BList 这个命名空间里面找而已。展不展开只是影响编译的时候,编译器的查找规则。
到这里我们就恍然大悟,为什么要加上 using namespace std; 是为了将标准库全局展开。std这个命名空间就是标准库专门构建的一道防线,防止自己定义的内容和标准库里面的起冲突,造成名字污染。那么如果随随便便就使用 using namespace std; 不就相当于将别人辛辛苦苦建立起来的 “长城” 给摧毁了吗?导致 std 这个命名空间形同虚设。所以,在做项目开发的时候,是不可以直接全局展开标准库的。但是如果是自己日常代码练习,就无所谓。
如下,将 using namespace std; 注释掉,使用cout 、endl 等等关键字,下面直接报错“未定义标识符”。也可以印证上面所说的,这是因为编译器不会去 std 这个命名空间寻找内容,所以找不到。
方法二
第二种方法是指定命名空间访问。这里涉及到一个运算符 :: 没错就是两个冒号,它的名字是域作用限定符。A::B,其含义是,B这个变量,指定到命名空间A去寻找。
给出如下代码,请问其结果是两个都输出 10 吗?第一个,根据局部优先原则,无疑是10。但是第二个输出,由于多了一个域作用限定符,会直接在该运算符左边的命名空间去寻找变量,左边命名空间是空,空实际上代表全局,所以第二个输出20。
int a = 20;
void f1()
{
int a = 10;
printf("%d\n", a);
printf("%d\n", ::a);
}
如下代码,也可以顺利输出 Hello World!不需要全局展开标准库,直接使用域作用限定符去std命名空间查找。但是这里也会产生一个问题,cout 和 endl 是经常要使用到的,所以如果不全局展开,每次都指定命名空间访问,会特别麻烦,所以有了第三种方法,部分展开。
#include<iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
return 0;
}
方法三
部分展开就很好用,比如对于cout,部分展开之后,可以直接使用,不需要用 :: 了。如下,using 命名空间::成员 即可。
4.嵌套使用
当然了,命名空间也是可以嵌套的,如下图,命名空间 BList 里面嵌套使用命名空间B,在右边的 main 函数里面,可以多重嵌套使用。
关于命名空间和using namespace std; 就介绍到这里,相信看完本文对它们的理解一定有所加深!