C++中数据的共享与保护之——标识符的作用域与可见性

作用域讨论的是标识符的有效范围,可见性讨论的是标识符是否可以被引用。我们知道,在某个函数中声明的变量就只能在这个函数中起作用,这就是受变量的作用域与可见性的限制。作用域与可见性既相互联系又存在着很大差异。

作用域


作用域是一个标识符在程序正文中有效的区域。C++中标识符的作用域有函数原型作用域、局部作用域(块作用域)、类作用域和命名空间作用域。

1.函数原型作用域

        函数原型作用域是C++程序中最小的作用域。第3章中介绍过,在函数原型中一定要包含形参的类型说明。在函数原型声明时形式参数的作用范围就是函数原型作用域。例如,有如下函数声明:

double area (double radius) ;

标识符radius的作用(或称有效)范围就在函数area形参列表的左右括号之间,在程序的其他地方不能引用这个标识符。因此标识符radius的作用域称做函数原型作用域。

2.局部作用域

        这里,在函数fun的形参列表中声明了形参a,在函数体内声明了变量b,并用a的值初始化 b。接下来,在if语句内,又声明了变量c。a,b和c都具有局部作用域,只是它们分别属于不同的局部作用域。
函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。因此,形参a的作用域从a的声明处开始,直到fun函数的结束处为止。函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的大括号为止。所谓块,就是一对大括号括起来的一段程序。在这个例子中,函数体是一个块,f语句之后的分支体又是一个较小的块,二者是包含关系。因此,变量b的作用域从声明处开始,到它所在的块(即整个函数体)结束处为止,而变量c的作用域从声明处开始,到它所在的块,即分支体结束为止。具有局部作用域的变量也称为局部变量。
3.类作用域

        先看一个例子:

这里,在函数fun的形参列表中声明了形参a,在函数体内声明了变量b,并用a的值初始化 b。接下来,在if语句内,又声明了变量c。a,b和c都具有局部作用域,只是它们分别属于不同的局部作用域。
        函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。因此,形参a的作用域从a的声明处开始,直到fun函数的结束处为止。函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的大括号为止。所谓块,就是一对大括号括起来的一段程序。在这个例子中,函数体是一个块,if语句之后的分支体又是一个较小的块,二者是包含关系。因此,变量b的作用域从声明处开始,到它所在的块(即整个函数体)结束处为止,而变量c的作用域从声明处开始,到它所在的块,即分支体结束为止。具有局部作用域的变量也称为局部变量。

3.类作用域 

类可以被看成是一组有名成员的集合,类X的成员m具有类作用域,对m的访问方式有如下3种。

  1. 如果在x的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。也就是说m在这样的函数中都起作用。
  2. 通过表达式x.m或者X::m。这正是程序中访问对象成员的最基本方法。X::m的方式用于访问类的静态成员。
  3. 通过ptr—>m 这样的表达式,其中ptr为指向X类的一个对象的指针。

C++中,类及其对象还有其他特殊的访问和作用域规则,在后续章节中还会深入讨论。

4.命名空间作用域

        为了介绍命名空间作用域,首先需要介绍命名空间的概念。一个大型的程序通常由不同模块构成,不同的模块甚至有可能是由不同人员开发的。不同模块中的类和函数之间有可能发生重名,这样就会引发错误。这就好像上海和武汉都有南京路,如果在缺乏上下文的情况下直接说出“南京路”,就会产生歧义。但如果说“上海的南京路”或“武汉的南京路”,歧义就会消除。命名空间起到的就是这样的作用。
命名空间的语法形式如下:

namespace 命名空间名{
    命名空间内的各种声明(函数声明、类声明、…)
} 

        一个命名空间确定了一个命名空间作用域,凡是在该命名空间之内声明的,不属于前面所述各个作用的标识符,都属于该命名空间作用域。在命名空间内部可以直接引用当前命名空间中声明的标识符,如果需要引用其他命名空间的标识符,需要使用下面的语法:

命名空间名∷标识符名

例如:

namespace SomeNs{
    class SomeClass{…};
};

如果需要引用类名SomeClass或函数名someFunc,需要使用下面的方式:

SomeNs::SomeClass objl;    //声明一个 SomeNs::SomeClass型的对象 objl    

        有时,在标识符前总使用这样的命名空间限定会显得过于冗长,为了解决这一问题, C++又提供了 using 语句,using语句有两种形式:

using 命名空间名:标识符名; 
using namespace 命名空间名:

前一种形式将指定的标识符暴露在当前的作用域内,使得在当前作用域中可以直接引用该标识符;后一种形式将指定命名空间内的所有标识符暴露在当前的作用域内,使得在当前作用域中可以直接引用该命名空间内的任何标识符。

        事实上,C++标准程序库的所有标识符都被声明在std命名空间内,前面用到的cin、 cout、endl 等标识符皆如此,因此,前面的程序中都使用了using namespace std。如果去掉这条语句,则引用相应的标识符需要使用std::cin、std::cout、std::endl 这样的语法。命名空间也允许嵌套,例如:

namespace OuterNs {
    namespace InnerNs{
        class SomeClass {…};
}

引用其中的SomeClass类,需要使用OuterNs::InnerNs::SomeClass的语法形式。
        此外,还有两类特殊的命名空间全局命名空间匿名命名空间。全局命名空间是默认的命名空间,在显式声明的命名空间之外声明的标识符都在一个全局命名空间中。匿名命名空间是一个需要显式声明的没有名字的命名空间,声明方式如下:

namespace {
    匿名命名空间内的各种声明(函数声明、类声明、.)
}

在包含多个源文件的工程中,匿名命名空间常常被用来屏蔽不希望暴露给其他源文件的标识符,这是因为每个源文件的匿名命名空间是彼此不同的,在一个源文件中没有办法访问其他源文件的匿名命名空间。

示例:

例5-1中所声明的全局变量就具有命名空间作用域,它们在整个文件中都有效。例5-1作用域实例。//5_1.cPP
#include <iostream> 
using namespace std;
int i;                          //在全局命名空间中的全局变量    
namespace Ns {
    int j;                     //在Ns命名空间中的全局变量    
}
int main () 
{
    i=5;                        //为全局变量i赋值    
    Ns::j=6;                    //为全局变量了赋值    
    {    //子块1    
        using namespace Ns;     //使得在当前块中可以直接引用Ns命名空间的标识符    
        int i;                  //局部变量,局部作用域    
        i=7;
        cout<<"i="<<i<<endl;    //输出7 
        cout<<"j="<<j<<endl;    //输出6
       }
    cout<<"i="<<i<<endl;        //输出 5    
    return 0;
}

运行结果:
i=7 
j=6 
i=5

        在这个例子中,变量i具有命名空间作用域,它属于全局命名空间,其有效作用范围到文件尾才结束。在主函数开始处给这个具有命名空间作用域的变量赋初值5,接下来在子块1中又声明了同名变量并赋初值7。第一次输出的结果是7,这是因为具有局部作用域的变量i把具有命名空间作用域的变量i隐藏了,具有命名空间作用域的变量i变得不可见(这是下面要讨论的可见性问题)。当程序运行到块1结束后,进行第二次输出时,输出的就是具有命名空间作用域的变量i的值5。变量j也具有命名空间作用域,它被声明在命名空间Ns中,在主函数中通过Ns::j的方式引用,为其赋值,接下来在块1中,通过using namespace Ns使得该命名空间的标识符可以在该块中被直接引用,因此输出j时可以直接使用标识符j。
        具有命名空间作用域的变量也称为全局变量。

可见性

下面,从标识符引用的角度,来看标识符的有效范围,即标识符的可见性。

程序运行命名空间作用域到某一点,能够引用到的标识符,就是该处可见的标识符。为了理解可见性,先来看一看不同作用域之间的关系。命名空间作用域最大,接下来是类作用域和局部作用域。上图描述了作用域的一般关系。可见性表示从内层作用域向外层作用域“看”时能看到什么。因此,可见性和作用域之间有着密切的关系。

作用域可见性的一般规则如下。

  • 标识符要声明在前,引用在后。
  • 在同一作用域中,不能声明同名的标识符。
  • 在没有互相包含关系的不同的作用域中声明的同名标识符,互不影响。
  • 如果在两个或多个具有包含关系的作用域中声明了同名标识符,则外层标识符在内层不可见。

        再看一下例5-1,这是命名空间作用域与局部作用域相互包含的实例,在主函数内块1之外,可以引用具有命名空间作用域的变量,也就是说它是可见的。当程序运行进入块1后,就只能引用具有局部作用域的同名变量,具有命名空间作用域的同名变量被隐藏了。
        提示        作用城和可见性的原则不只适用于变量名,也适用于其他各种标识符,包括常量名、用户定义的类型名、函数名、枚举类型的取值等。 

该章节论文摘自:《C++程序设计基础教程》郑莉 董渊 编著

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值