当前课程知识点:C++语言程序设计基础 > 第5章 数据的共享与保护 > 标识符的作用域与可见性 > 标识符的作用域与可见性
大家好
欢迎回来继续学习
C++语言程序设计
这一节我们来学习
标识符的作用域与可见性
因为我们
需要在不同的程序模块之间共享数据
所以我们就需要知道
我们要把这些需要共享的数据
定义在什么位置
才能合法地将这些数据
在我们需要的模块之间进行共享
那么作用域有多少种呢
由小到大的范围它分成
函数原型作用域
块作用域 也叫局部作用域
还有类作用域 文件作用域
以及命名空间作用域
命名空间我们将在第十章
对大家做更详细的介绍
那在这一章呢
我们主要关注于
前面这几种作用域
那么函数原型作用域
是这里面范围最小的作用域
它存在于哪儿呢
实际上函数原型作用域
就是函数的形参表的范围
也就是说
它起始于函数形参表的左括号
结束于函数形参表的右括号
这样一个小范围内作用域
叫做函数原型作用域
现在大家知道
为什么在声明函数原型的时候
你可以只给出这些参数的类型
可以根本不列出参数的名字
但是我们说呢
编译器它在编译
在理解这个函数原型的时候
它只介意参数表中
参数的类型以及它们的次序
并不介意参数名是什么
为什么呢
因为它作用域就这么小
就在这个参数表的括号之内
那么在这个之内呢
并不使用名字
离开这个函数原型声明呢
那它的作用域就结束了
所以我们写名字
或者不写名字都是可以的
只不过我们声明函数原型的时候
最好还是给参数写上名字
为什么呢
这个可读性比较好
虽然编译器不介意
但是程序员需要看到名字
才知道含义
好 现在我们来看这个例子
这是一个函数原型声明的例子
我们在这里面呢
声明了一个double类型的形参
radius
那我们看
它的作用域就在这一对括号中
不能把这个radius
用于程序正文的其他地方去
那有的同学可能有疑问
我在定义函数的时候
那形参
不是都用在函数体里面了吗
这是函数原型声明
跟定义是不一样的
定义的时候
那函数的形参实际上是具有
局部作用域的局部变量
这个在后面我们会看到的
接下来呢
我们再来看什么是局部作用域
我们在一个函数体中
定义的变量对象
都是具有局部作用域的
这个局部是哪儿呢
就是函数体的这一对大括号之内
在函数体之内
我们可以在任何地方需要的时候
再用一对大括号
去界定一个更小的模块
在这个更小的模块中
定义的变量对象
它也是具有局部作用域的
这个局部就在紧紧包围着
这个定义的
最内层的这一对大括号范围之类
还有就是函数的形参
它也是具有局部作用域的
它的作用域范围
在这个函数内部
下面我们来看这样一个例子
大家看
在这个函数定义里面呢
有形参a
那么a的作用域呢
是从函数域开始的地方
从它形实结合的时候
就给它初始化了
也就是在那个时候
给它分配内存空间初始化
那么一进入函数
这个a就诞生了
那么它的作用域
是在整个fun函数之内的
接下来呢
在函数体里面
又定义了一个变量b
它的作用域是从它的定义点开始
到整个函数结束
也是一个具有局部作用域的变量
那好
接下来我们看
在这个if语句的
if分支这个块里面
因为有一对大括号又括起来了
这个里面定义的这个c
它也是局部作用域
但是这个局部呢
它就更小一些
它仅仅存在于if这个块里面
离开紧紧包围它的最内层
大括号以后
它就不能起作用了
那么在类中定义的标识符呢
它都具有类作用域
类作用域首先它包括整个类体
当然它还不仅仅包括整个类体
我们知道类里面的很多成员函数
是在类体之外实现的
也就是说函数体的定义
很可能是在类体之外
那么类作用域呢就包括类体
以及在类外
实现的本类成员函数的函数体
这个都是类作用域
也就是说
类中定义的标识符
在这个范围之内
是可以访问的
可以起作用的
那么如果要在类外访问
具有类作用域的标识符
怎么访问呢
我们前面看到很多例题
它需要用对象名.成员名的方式
来访问 对吧
而且这个访问
还要受你定义这些标识符的时候
给它指定的访问控制权限的控制
比如说我们指定为private
那这样的标识符在类外
就不可以访问
如果指定为public
那么它在类外就可以访问
在类外
我们不仅可以通过对象名来访问
类作用域里面的标识符
还可以通过对象的引用来访问
在第六章我们还要介绍
可以通过指向对象的指针来访问
还有我们这一章
要给大家介绍静态成员
那么如果你定义了类的静态成员
在类外你还可以通过类名来访问
那么这就是类作用域它的特点
如果我们定义的标识符
不在上述各个作用域之中
那么它就存在于文件作用域之中
文件作用域就是
你定义这个标识符
所在的C++文件
文件作用域也称为静态作用域
在文件作用域中
定义的各个标识符
它的作用域开始点
是从它的定义点开始
在整个文件范围之内
都是有效的
如果在这个文件中
有多个函数的话
那么在它们中间都可以访问
这个文件作用域的
这样的标识符
不在前述各个作用域中的标识符
它就存在于文件作用域中
文件作用域起始于声明点
结束于文件结束的地方
那除了我们要知道
一个标识符的作用域以外呢
我们还有必要知道它的可见性
所谓可见性
也就是说
这个标识符在某个地方
即使它在作用域范围之类
我们还要再看
它是不是可以看得见的
就是很形象地说
是看得见的
只有可见的
你才可以去访问它
那么可见性呢
实际上就是
对标识符的引用的角度
来谈的这种概念
表示从内层作用域
向外层作用域看的时候
能看见些什么
如果这个标识符在某处可见
那么我们就可以在这处
引用这个标识符
如果你看都看不见
当然即使这个标识符
是在作用域范围之内
你也用不了
我们这里画了这么一个
简单的示意图
最内层就是局部作用域
也叫做块作用域
然后类作用域
外层的命名空间作用域
我们以后会讲
就是说我们从内向外看
能看见哪些
什么叫做从内向外看呢
为什么从内向外看
还有可能看不见呢
是这样的
如果某个标识符在外层中声明了
那么在内层中
它是不是可以用呢
前提条件是
如果在外层中声明了
在内层中没有同名的标识符声明
那当然外层中声明的标识符
在内层中是可见的
但是两个嵌套的作用域
如果在内层作用域中
声明了与外层作用域中的
同名的标识符
也就是名字重了
内外层标识符名字如果重了
那么内层标识符
就会把外层的同名标识符
给屏蔽了
也就是说外层作用域的标识符
在内层就不可见了
不可见 当然你就没法用它了
但是在不在它作用域范围之类呢
还在它作用域范围之内
所以我们既要了解作用域
又要了解它的可见性
下面我们就来通过一个例题
体会一下标识符的作用域
和可见性
现在我们看一下
在这个例题中呢
在主函数之外
定义了一个全局变量
int类型的变量i
它的作用域是整个文件作用域
也就是说
在整个这个cpp文件中
它都是有效的
接下来呢进到主函数里面
在主函数的函数体中
我们给i赋值
那这个时候访问的是哪个变量呢
还是这个全局变量
因为在主函数里面
也是全局变量的作用域范围之内
不过接下来
在主函数中我们又用一对大括号
括起了一个局部的程序模块
这三句话
在这个局部范围内
我们又定义了一个同名变量i
这个i就是一个局部变量了
也就是说
目前我们
在更小的作用域范围之内
定义了一个变量
它与外层作用域中的一个变量
重名了
这种情况下呢
外层作用域的变量
在内层就被屏蔽了
也就是说
在这个小的范围内
在这一对大括号里面
如果我们要访问i的话
它就不是那个全局变量了
就是访问到的只能是
这个局部变量i
所以我们现在给i复一个值7
然后再输出一下这i
验证一下
我们输出以后i是7
当离开这一对大括号以后
离开这个局部作用域以后
这个在这一对大括号里面
定义的这个i它就消亡了
因为局部变量
它的作用域
就在定义它的这个模块里面
离开这对大括号
它就消亡了
也就是说
它的存储空间就被释放了
这个变量就不存在了
所以在这个时候我们再次访问i
这时候访问的是哪个i呢
我们输出验证一下
看到输出的是5
是当初复给全部变量的这个值
所以现在我们访问的又是
全局变量i了
所以从这个例子我们可以看到
全局变量它的作用域
覆盖整个文件的范围
但是如果在内层作用域中
有同名的变量的话
就会把外层作用域的同名变量
屏蔽了
那么在离开了这个
最里面这一对大括号以后
虽然还是在主函数里面
在一个局部范围里面
函数的局部范围里面
但是因为在这个范围里面
目前没有同名的变量
去屏蔽这个全局变量i
所以我们还是能看到
这个全局变量i
-导学
--第1章导学
-计算机系统简介
--计算机系统简介
--计算机系统简介 测试题
-计算机语言和程序设计方法的发展
--计算机语言和程序设计方法的发展 测试题
-面向对象的基本概念
--面向对象的基本概念 测试题
-程序的开发过程
--程序的开发过程
--程序的开发过程 测试题
-信息的表示和储存
--计算机的数字系统
--数据的编码表示
--信息的表示和储存 测试题
-实验指导
-导学
--第二章导学
-C++语言概述
--C++语言概述 测试题
-基本数据类型、常量、变量
--程序举例
--基本数据类型、常量、变量 测试题
-运算与表达式
--运算与表达式 测试题
-实验二:简单程序设计(上)
-数据的输入和输出
--数据的输入和输出
--数据的输入和输出 测试题
-选择结构
--if语句
--switch语句
--选择结构 测试题
-循环结构
--for语句
--循环结构 测试题
-自定义类型
--自定义类型
--自定义类型
-第2章小结
--第二章小结
-实验二:C++简单程序设计(下)
-导学
--导学
-函数定义
--函数定义
--函数定义 测试题
-函数调用
--例3-2
--例3-3
--例3-4
--例3-5
--例3-6
--函数调用 测试题
-嵌套与递归
--例3-9
--例3-10
--嵌套与递归 测试题
-函数的参数传递
--函数的参数传递
--函数的参数传递 测试题
-引用类型
--引用类型 测试题
-含有可变参数的函数
--含有可变参数的函数 测试题
-内联函数
--内联函数 测试题
-constexpr函数
--CONSTEXPR函数课后习题
-带默认参数值的函数
--带默认参数值的函数 测试题
-函数重载
--函数重载 测试题
-C++系统函数
--C++系统函数习题
-第3章小结
--第三章小结
-实验三(上)函数的应用
-实验三(下)函数的应用
-导学
--导学
-面向对象程序的基本特点
--面向对象程序的基本特点 测试题
-类和对象
--类和对象的定义
--类和对象 测试题
-构造函数
--构造函数基本概念
--委托构造函数
--复制构造函数
--构造函数 测试题
-析构函数
--析构函数
--析构函数 测试题
-类的组合
--类的组合
--类的组合程序举例
--前向引用声明
--类的组合 测试题
-UML简介
--UML简介
--UML简介课后习题
-结构体与联合体
--结构体与联合体 测试题
-枚举类
--枚举类
--枚举类 测试题
-第4章小结
--第四章小结
-实验四(上)
--实验四(上)
-实验四(下)
--实验四(下)
-导学
--导学
-标识符的作用域与可见性
--标识符的作用域与可见性 测试题
-对象的生存期
--对象的生存期
--对象的生存期 测试题
-类的静态成员
--类的静态成员 测试题
-类的友元
--类的友元 测试题
-共享数据的保护
--共享数据的保护 测试题
-多文件结构和预编译命令
--多文件结构和预编译命令 测试题
-第5章小结
--小结
-实验五
--实验五
-导学
--导学
-数组的定义与初始化
--数组的定义与使用
--一维数组应用举例
--数组的定义与初始化 测试题
-数组作为函数的参数
--数组作为函数的参数 测试题
-对象数组
--对象数组
--对象数组 测试题
-基于范围的for循环
-指针的定义和运算
--指针的定义和运算 测试题
-综合实例
--综合实例
-实验六(上)
--实验六上
-指针与数组
--指针数组
--指针与数组 测试题
-指针与函数
--指针类型的函数
--指向函数的指针
--指针与函数 测试题
-对象指针
--对象指针
--对象指针 测试题
-动态内存分配
--动态内存分配 测试题
-智能指针
--智能指针
-vector对象
--vector对象
--vector对象 测试题
-对象复制与移动
--移动构造
--对象复制与移动 测试题
-字符串
--C风格字符串
--string类
--字符串 测试题
-第6章小结
--第六章小结
-综合实例
--综合实例
-实验六(下)
--实验六(下)