C++标识符的作用域与可见性

news2024/12/23 18:32:25

标识符是一个由程序员定义的名称,为标识区别变量、函数和数据类型等,代表程序的某些元素,变量名就是标识符的一个展现。
作用域讨论的是标识符的有效范围可见性讨论的是标识符是否可以被引用。在一个函数中声明的变量就只能在这个函数中起作用,这就是受变量的作用域和可见性的限制。作用域与可见性既相互联系又存在很大差异。

1.作用域

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

(1)函数原型作用域

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

double area(double radius);

标识符radius的作用(或称有效)范围就在函数area形参列表的左右括号之间,在程序的其他地方不能引用这个标识符。因此标识符radius的作用域称为函数原型作用域。
【注意】由于在函数原型的形参列表中起作用的只是形参类型,标识符并不起作用,因此是允许省去的。但考虑到程序可读性,通常还是要在函数原型声明时给出形参标识符。

(2)局部作用域

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

(3)类作用域

类可以被看成是一组有名成员的集合,类X的成员m具有类作用域,对m的访问方式有如下三种:
①如果X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。也就是说类的数据成员m在这样的函数中都起作用。
X的成员函数中没有声明与类中数据成员同名的局部作用域标识符的情况:

class X
{
public:
	X(int m = 0):m(m){}
	int getM()
	{
		//int m=7;
		return m;
	}
private:
	int m;
};
int main()
{
	X b(5);
	b.getM();
	cout << b.getM() << endl;
}

运行结果:
在这里插入图片描述
X的成员函数中声明了与类中数据成员同名的局部作用域标识符的情况:

class X
{
public:
	X(int m = 0):m(m){}
	int getM()
	{
		int m=7;
		return m;
	}
private:
	int m;
};
int main()
{
	X b(5);
	b.getM();
	cout << b.getM() << endl;
}

运行结果:
在这里插入图片描述
②通过表达式x.m或者X::m。这正是程序中访问对象的最基本方法。X::m的方式用于访问类的静态成员。
③通过ptr->m这样的表达式,其中ptr为指向X类的一个对象指针。
C++中,类及其对象还有其他特殊的访问和作用域规则。

(4)命名空间作用域

命名空间的概念:
在C/C++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对命名空间的。
命名空间的定义:
定义命名空间,需要使用到namespace 关键字,这个关键字的作用就是定义一个命名空间。
命名空间的语法形式如下:

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

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

命名空间名::标识符名

例如:
定义一个命名空间

namespace SomeNs
{
	class SomeClass
	{
    	...
    };
    void somemeFunc()
    {
    	...
    }
}

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

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

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

using 命名空间::标识符名;//1
using namespace 命名空间名;//2

第一种形式将指定的命名空间中的标识符暴露在当前的作用域中,使得在当前作用域中可以之间引用该标识符;第二种形式将指定命名空间内的所有标识符暴露在当前的作用域中,使得在当前作用域中可以直接引用该命名空间内的任何标识符。
事实上,C++标准程序库的所有标识符都被声明在std命名空间内,平时所用到的cincoutendl等标识符都是如此,因此,前面的程序中都使用了using namespace std;。如果去掉这条语句,则引用相应的标识符需要使用std::cinstd::coutstd::endl这样的语法。
命名空间也允许嵌套,例如:

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

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

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

在包含多个源文件的工程中,匿名命名空间常常被用来屏蔽不希望暴露给其他源文件的标识符,这是因为每个源文件的命名空间是彼此不同的,在一个源文件中没有办法访问其他源文件的匿名命名空间。
【例】作用域实例,以下声明的全局变量就具有命名空间作用域,它在整个文件中都有效。

int i;//在全局命名空间中的全局变量
namespace Ns
{
	int j;//在Ns命名空间中的全局变量
}
int main()
{
	i = 5;//为全局变量i赋值
	Ns::j = 6;//为Ns命名空间中的全局变量j赋值
	{   //子块1
		using namespace Ns;//使得在当前块中可以直接引用Ns命名空间中的标识符
		int a;    //局部变量,局部作用域
		a = 7;    
		cout << "a=" << a<< endl;//输出7
		int i = 8; //局部变量,局部作用域
		cout << "j=" << j << endl;//输出6
		cout << "i=" << i << endl;//输出8
	}
	cout << "i=" << i << endl;//输出5
	return 0;
}

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

2.可见性

从标识符引用的角度,来看标识符的有效范围,即标识符的可见性。程序运行到某一点,能够引用到的标识符,就是该处可见的标识符。

不同作用域之间的关系:

命名空间作用域最大,接下来依次是类作用域和局部作用域。如下图所示:
在这里插入图片描述
可见性表示从内层作用域向外层作用域“看”时能看到什么,可见性和作用域之间有着密切的关系。

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

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

int i;//在全局命名空间中的全局变量
namespace Ns
{
	int j;//在Ns命名空间中的全局变量
}
int main()
{
	i = 5;//为全局变量i赋值
	Ns::j = 6;//为Ns命名空间中的全局变量j赋值
	{   //子块1
		using namespace Ns;//使得在当前块中可以直接引用Ns命名空间中的标识符
		int i = 8; //局部变量,局部作用域
		cout << "i=" << i << endl;//输出8
	}
	cout << "i=" << i << endl;//输出5
	return 0;
}

运行结果:
在这里插入图片描述

上例是命名空间作用域与局部作用域互相包含的实例,在主函数内块1之外,可以引用具有该命名空间作用域的变量,也就是说它是可见的。当程序进入块1后,就只能引用具有局部作用域的同名变量,具有命名空间作用域的同名变量被隐藏了。这也是为什么两次输出的变量i的值不同。
【注意】作用域和可见性的原则不只适用于变量名,也适用于其他各种标识符,包括常量名、用户定义的类型名、函数名、枚举类型的取值等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/817549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue中使用代码编辑器 vue2-ace-editor

npm install --save-dev vue2-ace-editor// 全局引入 main.jsimport Editor from vue2-ace-editor; Vue.use(Editor)//组件中引入import Editor from vue2-ace-editor; components: {Editor, },<template><div class"codeEditBox"><editorv-model&qu…

2353. 设计食物评分系统;1895. 最大的幻方;842. 将数组拆分成斐波那契序列

2353. 设计食物评分系统 核心思想&#xff1a;首先明确我们有哪些功能&#xff0c;首先是修改某种食物分数的功能&#xff0c;然后第二点是能够每次弹出分数高字典序小的食物名字。由这两个我们想到了a 食物[分数],b 烹饪方式[分数&#xff0c;食物名字] 然后有一点经验的感…

小程序基础笔记

一、小程序与普通网页开发的区别 1、运行环境不同 网页运行在浏览器环境中小程序运行在微信环境中 2、API 不同 由于运行环境的不同&#xff0c;所以小程序中&#xff0c;无法调用 DOM 和 BOM 的 API。但是&#xff0c;小程序中可以调用微信环境提供的各种 API&#xff0c;例如…

初识低代码开发平台

2019年开始&#xff0c;低代码爆火&#xff0c;有人唱衰有人捧&#xff0c;反对的人认为对于那些真正的程序员来说&#xff0c;简直就是毒瘤&#xff0c;只是炒作概念而已&#xff0c;等尘埃落地&#xff0c;肯定一地鸡毛。 但是对于那些缺技术、缺人才&#xff0c;又需要数字…

ERROR:No tf data. Actual error: Fixed Frame [map] does not exist 解决办法

问题场景&#xff1a; 使用rviz时&#xff0c;出现warning&#xff0c;并且地图无法加载&#xff0c;如下所示&#xff1a; 原因分析&#xff1a; 之所以地图无法加载出来&#xff0c;其主要原因是tf树中没有world坐标系&#xff0c;解决方法就是让rviz知道world坐标系在哪…

短视频平台视频怎么去掉水印?

短视频怎么去水印&#xff0c;困扰很多人&#xff0c;例如&#xff0c;有些logo水印&#xff0c;动态水印等等&#xff0c;分享操作经验&#xff1a; 抖音作为中国最受欢迎的社交娱乐应用程序之一&#xff0c;已成为许多人日常生活中不可或缺的一部分。在使用抖音过程中&#x…

《TCP IP网络编程》第十四章

第 14 章 多播与广播 14.1 多播 多播&#xff08;Multicast&#xff09;方式的数据传输是基于 UDP 完成的。因此 &#xff0c;与 UDP 服务器端/客户端的实现方式非常接近。区别在于&#xff0c;UDP 数据传输以单一目标进行&#xff0c;而多播数据同时传递到加入&#xff08;注…

【奥比中光Gemini 2L快速上门】

奥比中光Gemini 2L快速上手 目录 奥比中光Gemini 2L快速上手[TOC](目录) 一、下载配置环境1.1 官网下载SDK1.2 配置环境 二、测试2.1 在bin中运行示例2.2 配置cmake 三、CMAKE3.1 CmakeLists.txt中各设置的意义 一、下载配置环境 1.1 官网下载SDK 进入官网&#xff0c;下载名…

maven的下载安装与配置环境变量!!!(全网最详细)

1.maven 官方网站&#xff0c;http://maven.apache.org 2.去官网下载。 3.选择你自己的解压路径&#xff08;D:\maven【我的挤压路径】&#xff09; 4.配置jdk环境变量&#xff08;不会的小伙伴可以看我以前的博客&#xff09; jdk 1.8 安装配置环境变量_明天更新的博客-CSD…

详细介绍Webpack5中的Plugin

Plugin的作用 插件Plugin可以扩展webpack&#xff0c;加入自定义的构建行为&#xff0c;使 webpack 可以执行更广泛的任务&#xff0c;拥有更强的构建能力。 Plugin的工作原理 webpack就像一条生产线&#xff0c;要经过一系列处理流程后才能将源文件转换成输出结果。 这条生…

Linux系统下MySQL读写分离

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、基于Amoeba读写分离 1.基于程序代码内部实现 2.基于中间代理层实现 三、操作步骤 1.在主机Amoeba上安装java环境 2.安装并配置Amoeba 3.配置Amoeba读写分离…

matlab RRR机械臂 简略代码

RRR机器人&#xff01;启动&#xff01; gazebo在arm mac上似乎难以运行&#xff0c;退而选择Matlab&#xff0c;完成老师第一个作业&#xff0c;现学现卖&#xff0c;权当记录作业过程&#xff0c;有不足之处&#xff0c;多多指教。 作业&#xff01;启动&#xff01; RRR机…

WAF绕过-信息收集篇

WAF绕过主要集中在信息收集&#xff0c;漏洞发现&#xff0c;漏洞利用&#xff0c;权限控制四个阶段。 1、什么是WAF&#xff1f; Web Application Firewall&#xff08;web应用防火墙&#xff09;&#xff0c;一种公认的说法是“web应用防火墙通过执行一系列针对HTTP/HTTPS的安…

有效的随机圆检测

文章目录 0、 摘要&#xff1a;一、 Base Idea二、 Determining Possible Circle2.1 判别条件2.2 圆的判别 三、Determining True Circles四、The Proposed RCD五、改进六、参考在这里插入图片描述 有效的随机圆检测 0、 摘要&#xff1a; 参考的文章提出了一种有效的不基于霍…

一文教你搭建工程化开发环境!

搭建工程化开发环境 下载 Node.js 官方下载地址 https://nodejs.org/zh-cn/download/releases node.js 版本迭代的非常快&#xff0c;目前官方已经推出到 v19.2.0 版本了&#xff0c;相对是一个比较新的版本了。建议下载 v14.18.3 版本&#xff0c;至少这个版本目前在很多项…

【kubernetes系列】flannel之vxlan模式分析

概述 在Kubernetes中要保证容器之间网络互通&#xff0c;网络至关重要。而Kubernetes本身并没有自己实现容器网络&#xff0c;而是而是借助CNI标准&#xff0c;通过插件化的方式自由接入进来。在容器网络接入进来需要满足如下基本原则&#xff1a; Pod无论运行在任何节点都可…

有趣的Python之基本语法(一篇足够)

目录 Python简介 基本数据类型 进入交互模式 input()函数 条件语句 逻辑运算符 列表list 元组 字典 循环语句 format()方法和f 定义函数 python中的标准库引入 引入第三方库模块 面向对象 读文件 写文件 异常处理 Python简介 面向对象编程、函数式编程和过程…

二 动手学深度学习v2笔记 —— 线性回归 + 基础优化算法

二 动手学深度学习v2 —— 线性回归 基础优化算法 目录: 线性回归基础优化方法 1. 线性回归 总结 线性回归是对n维输入的加权&#xff0c;外加偏差使用平方损失来衡量预测值和真实值的差异线性回归有显示解线性回归可以看作是单层神经网络 2. 基础优化方法 梯度下降 小批量…

4通道高速数据采集卡推荐哪些呢

FMC141是一款基于VITA57.4标准的4通道2.8GSPS/2.5GSPS/1.6GSPS采样率16位DA播放FMC子卡&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.4与VITA57.1规范&#xff0c;16通道的JESD204B接口通过FMC连接器连接至FPGA的高速串行端口。 该板卡采用TI公司的DAC39J84芯片&#x…

【玩转Linux】Linux输入子系统简介

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…