目录
一 命名空间来源以及普通命名空间使用
随着学习终于到了c嘎嘎,开始学的几天确实有许多点没领悟到位,反复回顾才觉得没太多遗漏,现在我就分享一些我的感悟,一开始学的c++的知识都是一些对于c语言不足的方面进行的改进,比如命名冲突的问题,每个人变量,函数的命名都是只顾自己的,不可能每写一个函数,去查一下项目组里有没有人用过这个名字,在这种情况下非常容易造成命名冲突,所以c++就设计出了命名空间,命名空间就像是一个域一样,将自己的变量和函数与其它的独立开来,域这个概念其实c语言是有的,比如局部域和全局域,当局部域和全局域冲突时,局部域优先。接下来我们就用实例子分析命名空间。
#include<iostream>
using std::cout;
using std::endl;//只将常用的展开,展开全部会有命名冲突的风险
namespace num
{
int a = 2;
}
int a = 3;//全局变量
int main()
{
//命名空间尝试
int a = 1;//局部变量
域作用限定符::前加上命名空间的名字,表示到该命名空间去找变量a
cout << "指定命名空间查找:" << num::a << endl;
如果域作用限定符前未加命名空间的名字,则默认到全局域去找
cout << "不指定命名空间查找:" << ::a << endl;
不带域作用限定符时,则在默认先到局部域找,再去全局域,默认不会到命名空间域找
cout << "不带域作用限定符:" <<a << endl;
return 0;
}
输出结果如图
二 由此我们可以大致理解命名空间中成员的访问规则
1:展开整个命名空间,使得命名空间内函数暴露在全局域,当然既然是暴露在全局域,遵循一个域内不能同名的规则,
注意:展开整个命名空间会有一定风险,如果我们之后写了一个函数,如果这个和库里的函数命名冲突,但当时我们并没有包含对应的头文件,一旦之后包含了就会出现冲突,至于为什么一包含头文件就会冲突,我在第五点“c++包含了头文件,为什么还要展开命名空间”会详细提及。
2:使用时用域名限定符指定访问的域,在域名限定符前加上命名空间的名字,表示从该空间找,如果未加名字,默认到全局域找,然后到局部域找。
3.展开对应的命名空间成员
三 嵌套命名空间
当一个命名空间包含的变量和函数多了也有可能会出现冲突,所以我们还可以在命名空间再设一个命名空间,嵌套命名空间,接下来就用代码实现一下
namespace num2
{
int b = 3;
namespace num3
{
int b = 4;
}
}
int main()
{
//嵌套命名空间尝试
cout << "嵌套命名空间去内层找数字b的结果为" << num2::num3::b << endl;
cout << "嵌套命名空间去外层找数字b的结果为" << num2::b << endl;
return 0;
}
结果如下
四 同名命名空间
对于同名命名空间,语法规定是将其归为同一个命名空间,无论是在同一文件还是在不同文件。
五 c++的头文件都没有后缀.h原因?
我在入门c++的时候,发现c++的头文件都没有后缀.h,首先我认为是为了区分c的头文件和c++的头文件,因为c++的标准库都在std这个命名空间内,当别人看到你包含的头文件没有后缀.h时就知道这是c++的头文件,就会意识到使用对应函数要展开命名空间,这样编译器才会去命名空间找,至于为什么c++的库在命名空间呢?因为c++STL库比c++晚了七年才推出,而此时c++程序员已经定义了许多的变量和函数,而这些已经定义的变量和函数名称可能就和STL库冲突,那之后使用这个库的时候就会出现冲突,程序员修改起来太麻烦,所以委员会决定把STL库放进std这个命名空间,为了统一,总不能一部分头文件在命名空间,一部分又不在命名空间,那使用者就要区分哪些库在命名空间,使用时要展开,哪些库不在,使用时不用展开,也很麻烦,所以都丢到命名空间std内。
六 c++包含了头文件,为什么还要展开命名空间?
我在使用c++函数的时候发现包含头文件和展开命名空间总是成对出现,我十分困惑,查阅了许多资料,得出如下推导。
//#include<iostream>
//#include<cstdlib>
using namespace std;
int main()
{
int rand = 0;
cout <<rand<< endl;
return 0;
}
我认为是因为c++包含头文件是把函数声明和定义包含到源文件,包含的头文件如果是属于std部分的,拷贝到源文件的时候会在该内容外层又封一个namespace std,而要访问这个命名空间内的内容,就要用到先前访问命名空间成员的规则了,后面展开std也不是在那个包含了c++所有的库的std里找,而是在这个源文件新封的std里找函数定义。那如果没包含头文件,展开命名空间也找不到定义,这就是报错未声明未定义的原因,如果不展开命名空间,编译器也不会到命名空间去找,同样报错(想想也很合理,真正的std库里有那么多东西,在这里面查找多浪费时间,把自己包含的头文件封为新的std,然后在这里找,范围就大大减小了,效率必然会提高)
以上就能解释为什么只展开命名空间,不包含头文件却报错cout未声明,未定义,同理不展开命名空间,编译器也不会从该命名空间找函数,最后报错应该是类似的。
七.不包含头文件,用了库里的标识符名会如何?
当我理清了包含头文件和展开命名空间std的关系后,那是不是说明如果我用了一个库里的变量名,然后没包含头文件,这样在第六点提到的伪std就找不到该标识符定义,也就不会冲突,开发编译器的人也不傻,这种潜在风险还是会指出的。
下面为.cpp源文件
由于c++兼容c,所以上面会被当成c语言,编译器只会以c语言的要求检查你的代码,导致rand不会被说命名冲突,而只要代码里出现c++代码,那编译器就会以c++标准要求你的代码,我估计编译器会对代码做两重保障,一重是检查第六点中提到的伪std命名空间(命名空间内为我们包含的头文件)里是否有会和自己定义的标识符名发生冲突,第二重是编译器还会去真正的std(里面有c++的所有头文件的命名空间)去检查是否会和代码使用的标识符冲突,这样才体现c++语法的严格。
下面是.c源文件,编译器对c语言管的比较宽松,没有上面的第二重要求,所以下面代码能编译通过。
#include<stdio.h>
int rand = 0;
int main()
{
printf("%d", rand);
return 0;
}
这里涉及到的一些知识点本人也是查阅一些资料得知,能解释当前状况就停止探究了,还未深入理解编译器的原理,如有错误,可以私信交流讨论。