6.8.2 可见域
可见域是作用域的子集,我们可以这样理解,作用域指的是变量理论上有效的源代码区域,而可见域指的是该变量实际有效的内存区域,如果没有屏蔽发生,可见域和作用域应该是等价的。
1.屏蔽
先来看下面一个例子,见代码6.18。
代码6.18 内层标识符屏蔽外层同名标识符IDShield
<———————————文件名:example618.cpp————————————————> 01 #include<iostream> 02 using namespace std; 03 extern int A,B,C;//引用性声明,外部int变量A、B和C 04 int main() 05 { 06 cout<<"A:"<<A<<",B:"<<B<<",C:"<<C<<endl;//此时,访问的是外部全局变量 07 int B=1;//内部变量B屏蔽掉外部变量B 08 cout<<"A:"<<A<<",B:"<<B<<",C:"<<C<<endl;//输出验证 09 { 10 int B=2,C=3;//花括号(代码块)内又对代码块外的同名标识符构成屏蔽 11 cout<<"A:"<<A<<",B:"<<B<<",C:"<<C<<endl; 12 } 13 return 0; 14 } 15 int A=8,B=7,C=6;//定义性声明,外部int变量A、B和C
输出结果如下所示。
A:8,B:7,C:6 A:8,B:1,C:6 A:8,B:2,C:3
【代码解析】在代码第15行为全局变量A、B和C的定义性声明,这3个变量具备全局作用域,经过第3行的引用性声明后,理论上便可以使用在程序的任何地方(如A),但是,第7行声明的自动变量B具有块作用域,从第7行开始到第14行main()函数结束,这块区域是自动变量B的作用域,也是其可见域。但是,这会导致内层变量B屏蔽了外层变量B,使得全局变量B在第7~14行之间不可见。
因此就本例而言,应将第7~14行从全局变量B的可见域中除去,同理,第9~12行组成的块中,第10行声明的B和C屏蔽了第7行声明的B和全局变量C,全局变量C的可见域为除10、11和12行之外的代码区。
可以看出,一个内层的标识符可以屏蔽外层的同名标识符(包括同名的全局标识符),使其不可见,这时,应从外部标识符的可见域中刨除内部标识符的可见区域。
内部静态变量的作用域、可见域及屏蔽法则和自动变量一样,但对外部静态变量而言,需要强调的是,在一个文件中声明的外部静态变量,将屏蔽其他文件中定义的同名全局变量。但是,不允许在同一文件中定义同名的全局变量和外部静态变量。文件可以看成内层,而程序则是外层,这是屏蔽法则的一种特殊情况。
注意
只有在可见域内才能对变量进行合法的访问。
使用作用域分辨符:可使被屏蔽的全局变量在局部可见,如果将代码6.18中的第11行改为如下所示。
cout<<"A:"<<:A<<",B:"<<:B<<",C:"<<:C<<endl;输出结果将变为如下所示。 A:8,B:7,C:6 A:8,B:1,C:6 A:8,B:7,C:6
但是,不能使用:使被屏蔽的自动变量(如代码6.18中的第7行的B)在第10行和第12行之间可见。
2.全局变量引用声明位置与其可见域
全局变量只能有一个定义性声明,但允许有多个引用性声明,实际上,引用性声明的位置与其可见域有很大关系。
在块内对全局变量进行引用声明时,其可见性仅局限于该块,在该块外部访问全局变量时,编译器会给出变量未定义的错误提示,如代码6.19。
代码6.19 在块内对全局变量进行引用声明GlobalVariableAccess
<—————————————文件名:example619.cpp——————————————> 01 #include<iostream> 02 using namespace std; 03 int main() 04 { 05 { 06 extern int A;//全局变量A的引用性声明 07 cout<<"A:"<<A<<endl;//输出A 08 } 09 cout<<"A:"<<A<<endl;//此时,A不可见 10 return 0; 11 } 12 int A=7;//全局变量A的定义性声明编译链接,VC6给出的错误提示如下所示。 error C2065:'A':undeclared identifier
在外部(函数外部、类外部)对全局变量进行引用声明具有“文件可见性”,文件中,从声明处开始,到文件结束的任何位置都可合法访问该全局变量。
技巧
推荐将全局变量的引用性声明放在文件的头部。当然,如果全局变量和访问代码在同一文件中,且变量定义在前而访问在后,那么在本文件中,该变量的可见域为从变量定义到文件结束。
引用声明不能提供全局可见性(不具备跨文件性质),这意味着,在使用全局变量之前,必须在当前文件或当前块中进行引用声明。
同一个全局变量的引用声明可以有很多次,变量的可见域是所有引用声明提供的可见域的总和。
3.重复定义
除了屏蔽外,在同一个作用域内,变量不能同名,否则程序编译时,编译器会给出变量重复定义的错误。实际上,在源程序中用变量名指明变量,而在程序执行过程中,变量对应这一块内存空间,所以,编译器维护着变量名和内存地址的映射表,在特定的代码行中,变量名只能对应一块内存空间,否则,编译器无法决定该使用哪块地址空间,便给出变量重复定义的提示,这就是全局变量只能定义声明一次的原因。
对于变量屏蔽这种情况,编译器能根据所在块决定变量名对应的内存地址,因此不会报错。在代码6.19中,如果在第5行和第8行组成的块中声明自动变量A,会导致A重定义的错误。“extern int A;”语句通知编译器A代表的是外部全局变量,此时再声明一个自动变量A,编译器无法判断后续代码中用到的A对应哪块地址(静态存储区或栈),VC6会给出如下错误提示。
error C2086:'A':redefinition