C++名称空间详细解读

C++名称空间详细解读,第1张

预备概念 声明区域

可以在其中进行声明的区域,包括代码块,函数内部,单个文件,以及本文提到的命名空间。每个声明区域都可以声明名称,这些名称独立于在其他声明区域中声明的名称。

名称空间层次

不同的声明区域,其具备不同空间层次,这些层次由全局(各文件可共享/外部链接性),全局(各文件不可共享/内部链接性),函数内部,代码块(花括号括起来的)依次所区分的高低级别,低级别的空间层次将掩盖高空间层次的同名变量。但名称空间的层次在与上述层次比较时只区分全局跟非全局。

编译器编译过程&连接器链接过程

编译器处理的单元是单个cpp,因此在每个cpp中使用名称时,必须要让编译器知道定义了或者定义过(比如该cpp没定义,但别的cpp定义了)这个名称,否则在依次编译单个cpp时将会出错。编译器在对每个cpp进行编译时,会生成中间文件,这个中间文件是临时的,是记录该cpp定义了或定义过的相关名称以及可能对别的文件依赖的一种追踪信息。当到了链接阶段,将根据这个中间文件进行不同obj(每个cpp编译后生成的目标对象)的链接,使其成为有机整体(也就是exe)。

那么针对上述过程,名称的报错可能有俩种,第一种:不知道该cpp定义了或定义过某个名称亦或是知道了该cpp反复定义了同一个名称,在编译阶段就报错。第二种:知道在每个cpp定义了或定义过,但链接时根据中间文件发现,同一名称空间层次,定义了同一名称变量。俩种报错如下图所示:

编译时的名称错误

同一个cpp重复定义了

链接时的名称错误

不同cpp在同一名称空间层次定义了同名变量

潜在作用域

变量/函数的潜在作用域从声明点(不是定义点,当然定义也算是一种声明)开始到声明区域的结尾。

作用域

对程序而言可见的范围称为作用域。

下图展示潜在作用域与作用域的区别:

名称空间 名称空间的性质 在哪里定义名称空间

它也是声明区域,故其中声明的名称不会与其他声明区域里的同名名称相冲突,名称空间可以是全局定义,也可以在另一个名称空间中定义但不能在代码块中定义。你可以把名称空间想象成单个文件从名称空间定义处开始的平行空间,这个平行空间内部的名称遵守跟全局定义一样的声明规则

同一文件中如何使用名称空间

如下图所示,第一个图无法在命名空间外部使用变量a,第二个图使用using namespace A;使用命名空间,但发现没定义该命名空间,故得出:名称空间是单个文件从名称空间定义处开始的平行空间。因此必须在名称空间定义后,再使用using namespace A;方可。如第三个图所示:

不同文件如何使用同一名称空间 

然而在另一个cpp中使用a出现如下第一个图的报错,这就是为什么我说:名称空间是单个文件从名称空间定义处开始的平行空间。也就是说其他的文件无法获知某个在别处cpp定义的命名空间的具体定义。因此使用using namespace A;报错,解决办法就如同在别的cpp中定义全局变量,而需要使用他的文件只需要extern 声明一样。具体如第二个图:

名称空间具备开放性

名称空间允许在不同文件使用namespace   名称空间名   {加入的变量/函数/类等的定义}把名称加入到该名称空间。当然,在别的cpp中需要按照类似全局定义时的声明规则(变量,类定义,函数定义它们的规则不同)。一般如果在多个cpp中加入,则使用时会繁琐的在各个cpp中声明。所以一般来说名称空间定义会在一个.h文件中把其全部定义(如果是类的话,包括类定义与相应类成员函数一起定义了)

名称空间名中名称的链接性

默认外部链接性,除非声明的是常量类型(这点跟全局类似)。

如何使用名称空间中的名称 运算符::

通过名称空间名::名称进行使用。

using声明

通过using 名称空间名::名称进行声明,随后就可进行直接使用名称。如果函数被重载,则导入所有同名函数。

using编译指令

通过using namespace 名称空间名编译指令,随后就可使用名称空间中的所有名称。

三种方式的区别

第一种方式,最简单明了,且不会造成任何冲突,但使用起来比较繁琐。

第二种方式,它相当于将名称空间中的特定名称,融入到所处的声明区域,该名称的名称空间层级跟所处的声明区域将相同,如果该声明区域有同名变量,则会报错。

第三种方式,它将名称空间所有的名称在所处的声明区域进行降临,但这些名称所蕴含的名称空间层级仍然属于全局。如果所降临的声明区域有同名名称,将覆盖对应的降临的名称。

名称空间的嵌套定义

名称空间可以嵌套定义,也就是说一个名称空间可以在另一个名称空间中定义。如下图所示:

 在通过上述三种方式使用名称空间B里的名称时,需要对一次(这个具体与嵌套定义的层数有关)使用::运算符。

在名称空间中使用using声明与using编译指令

可以在某一个名称空间中使用using声明与using编译指令,就如同在文件中(全局名称空间)使用一样,需要注意的是,在使用时,需要相应的名称空间的定义或名称空间中即将要用到的名称的声明。比如下图所示:在一个cpp中定义了名称空间A,B在另一个cpp中定义A,其中导出a的变量,则a在该cpp中已知,但进一步使用using namespace B;出错,因为在这个cpp中,不知B是否为名称空间。在此请详细再看看文章开头编译与链接过程的解读,他可能让你对使用名称,声明名称,定义名称有新的感悟。

 对于上图最好的解决办法是,把名称空间A,B的定义放在.h文件中,然后使用#ifndef技术,然后在需要他们的cpp中包含该.h文件,最后就可以在名称空间A中使用using声明与using编译。当然我们不建议自己在任何cpp随意的扩充某个名称空间,这样会导致繁琐的声明与可能潜在的多次定义报错。在针对类跟类方法,函数声明跟函数定义,可以采取把函数声明与类定义放到.h中的名称空间,把具体定义与实现放到.cpp中的同名名称空间中。

using编译指令在不同位置的结果

using编译指令在全局使用与局部使用将有不同结果,如下图所示,在用于全局时,不会覆盖全局里的同名变量,因为名称空间如本文前面所述只区分全局与局部,因此它们属于同一名称空间级别。

using编译指令的可传递性

如果在一个名称空间A中使用了using编译指令对B进行 *** 作,则使用using编译指令对A *** 作相当于对两者分别使用using编译指令。

可以给名称空间创建别名

 namespace 别名=名称空间名;

未命名的名称空间名

省略了名称空间名,它提供了链接性为内部的静态变量的替代品。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/793575.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存