C++ Primer 类的作用域

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 7.4 类的作用域
    • 作用域和定义在类外部的成员
    • 名守查找与类的作用域
      • 用于类成员声明的名字查找
    • 类型名要特殊处理
    • 成员定义中的普通块作用域的名字查找
    • 类作用域之后,在外围的作用域中查找
    • 在文件中名字的出现处对其进行解析

7.4 类的作用域

每个类都会定义它自己的作用域。在类的作用域之外,普通的数据和函数成员只能由对象、引用或者指针使用成员访问运算符来访问。对于类类型成员则使用作用域运算符访问。不论哪种情况,跟在运算符之后的名字都必须是对应类的成员:

Screen::pos ht=24,wd=80;//使用Screen定义的pos类型
Screen scr(ht,wd,' ');
Screen *p=&scr;
char c = scr.get();//访问scr对象的get成员
c = p->get();//访问p所指对象的get成员

作用域和定义在类外部的成员

一个类就是一个作用域的事实能够很好地解释为什么当我们在类的外部定义成员函数时必须同时提供类名和函数名。在类的外部,成员的名字被隐藏起来了。

一旦遇到了类名,定义的剩余部分就在类的作用域之内了,这里的剩余部分包括参数列表和函数体。结果就是,我们可以直接使用类的其他成员而无须再次授权了。

例如,我们回顾一下Window_mgr类的clear成员,该函数的参数用到了Window_mgr类定义的一种类型:

void Window_mgr::clear(ScreenIndex i)
{
Screen&s=Screens[i];
s.contents=string(s.height*s.width,' ');
}

因为编详器在处理参数列表之前已经明确了我们当前正位于Window_mgr类的作用域中,所以不必再专门说明ScreenIndex是Window_mgr类定义的。出于同样的原因,编译器也能知道函数体中用到的screens也是在Window_mgr类中定义的。

另一方面,函数的返回类型通常出现在函数名之前。因此当成员函数定义在类的外部时,返回类型中使用的名字都位于类的作用域之外。这时,返回类型必须指明它是哪个类的成员。例如,我们可能向windowmgz类添加一个新的名为addscreen的函数,它负责向显示器添加一个新的屏幕。这个成员的返回类型将是ScreenIndex,用户可以通过它定位到指定的Screen:

class Window_mgr{
public:
//向窗口添加一个Screen,返回它的编号
ScreenIndex addScreen(const Screen&);
//其他成员与之前的版本一致
};
//首先处理返回类型,之后我们才进入Window_mgr的作用域
Window_mgr::ScreenIndex
Window_mgr::addScreen(const Screen&s)
{screens.push_back(s);return screens.size()-1;
}

因为返回类型出现在类名之前,所以事实上它是位于Window_mgr类的作用域之外的。在这种情况下,要想使用ScreenIndex作为返回类型,我们必须明确指定哪个类定义了它。

名守查找与类的作用域

在目前为止,我们编写的程序中,名字查找(name lookup)(寻找与所用名字最匹配的声明的过程)的过程比较直截了当:

  • 首先,在名字所在的块中寻找其声明语句,只考虚在名字的使用之前出现的声明。
  • 如果没找到,继续查找外层作用域。
  • 如果最终没有找到匹配的声明,则程序报错。

对于定义在类内部的成员函数来说,解析其中名字的方式与上述的查找规则有所区别,不过在当前的这个例子中体现得不太明显。类的定义分两步处理:

  • 首先,编译成员的声明。
  • 直到类全部可见后才编译函数体。

按照这种两阶段的方式处理类可以简化类代码的组织方式。因为成员函数体直到整个类可见后才会被处理,所以它能使用类中定义的任何名字。相反,如果函数的定义和成员的声明被同时处理,那么我们将不得不在成员函数中只使用那些已经出现的名字。

用于类成员声明的名字查找

这种两阶段的处理方式只适用于成员函数中使用的名字。声明中使用的名字,包括返回类型或者参数列表中使用的名字,都必须在使用前确保可见。如果某个成员的声明使用了类中尚未出现的名字,则编译器将会在定义该类的作用域中继续查找。例如:

typedef double Money;
string bal;
class Account{
public:
Money balance(){ return bal; }
private:
Money bal;
};

当编译器看到balance函数的声明语句时,它将在Account类的范围内寻找对Money的声明.编译器只考虑Account中在使用Money前出现的声明,因为没找到匹配的成员,所以编译器会接着到Account的外层作用域中查找.在这个例子中,编译器会找到Money的typedef语句,该类型被用作balance函数的返回类型以及数据成员bal的类型。另一方面,balance函数体在整个类可见后才被处理,因此,该函数的return语句返回名为bal的成员,而非外层作用域的string对象。

类型名要特殊处理

-般来说,内层作用域可以重新定义外层作用域中的名字,即使该名字已经在内层作用域中使用过。然而在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字:

typedef double Money;
class Account{
public:
Money balance(){ return bal;}//使用外层作用域的Money
private:
typedef double Money;//错误:不能重新定义Money
Money bal;
// ...
}

需要特别注意的是,即使Account中定义的Money类型与外层作用域一致,上述代码仍然是错误的。

尽管重新定义类型名守是一种错误的行为,但是编译器并不为此负责。一些编译器仍将顺利通过这样的代码,而忽略代码有错的事实。

类型名的定义通常出现在类的开始处,这样就能确保所有使用该类的成员都出现在类名的定义之后。

成员定义中的普通块作用域的名字查找

成员函数中使用的名字按照如下方式解析:

  • 首先,在成员函数内查找该名字的声明。和前面一样,只有在函数使用之前出现的声明才被考虑。
  • 如果在成员函数内没有找到,则在类内继续查找,这时类的所有成员都可以被考虑。
  • 如果类内也没找到该名字的声明,在成员函数定义之前的作用域内继续查找。

一般来说,不建议使用其他成员的名字作为某个成员函数的参数。不过为了更好地解释名字的解析过程,我们不妨在dummy_fcn函数中暂时违反一下这个约定:

//注意:这段代码仅为了说明而用,不是一段很好的代码
//通常情况下不建议为参数和成员使用同样的名字
int height;//定义了一个名字,程后将在Screen中使用
class Screen{
public:
typedef std::string::size_type pos;
void dummy_fcn(pos height){cursor = width*height;//哪个height?是那个参数
}
private:
pos cursor=0;
pos height=0,width=0;
};

当编译器处理dummy_fcn中的乘法表达式时,它首先在函数作用域内查找表达式中用到的名字。函数的参数位于函数作用域内,因此dummy_fcn函数体内用到的名字height指的是参数声明。

在此例中,height参数隐藏了同名的成员。如果想绕开上面的查找规则,应该将代码变为:

//不建议的写法:成员函数中的名字不应该隐藏同名的成员
void Screen::dummy_fcn(pos height){
cursor=width*this->height;//成员height
//另外一种表示该成员的方式
cursor= width* Screen::height;//成员height
}

尽管类的成员被隐藏了,但我们任然可以通过加上类的名字或者显示的使用this指针来强制访问成员。

其实最好的确保我们使用height成员的方法是给参数起个其他名字:

//建议的写法:不要把成员名字作为参数或其他局部变量使用
void Screen::dummy_fcn(pos ht){
cursor=width*height;//成员height
}

在此例中,当编译器查找名字height时,显然在dummy_fcn函数内部是找不到的。编译器接着会在Screen内查找匹配的声明,即使height的声明出现在dummy_fcn使用它之后,编诙器也能正确地解析函数使用的是名为height的成员。

类作用域之后,在外围的作用域中查找

如果编译器在函数和类的作用域中都没有找到名字,它将接着在外围的作用域中查找。在我们的例子中,名字height定义在外层作用域中,且位于screen的定义之前。然而,外层作用域中的对象被名为height的成员隐藏掉了。因此,如果我们需要的是外层作用域中的名字,可以显式地通过作用域运算符来进行请求:

//不建议的写法:不要隐藏外层作用域中可能被用到的名守
void Screen::dummy_fcn(pos height){cursor=width*::height;//哪个height?是那个全局的
}

尽管外层的对象被隐藏掉了,但我伴依然可以用作用域运算符访问它。

在文件中名字的出现处对其进行解析

当成员定义在类的外部时,名字查找的第三步不仅要考虑类定义之前的全局作用域中的声明,还需要考虑在成员函数定义之前的全局作用域中的声明。例如:

int height;//定义了一个名字,稍后将在Screen中使用
class Screen{
public:
typedef std::string::size_type pos;
void setHeight(pos);
pos height=0;//隐藏了外层作用域中的height
};Screen::pos verify(Screen::pos);
void Screen::setHeight(pos var){//var:参数//height:类的成员//verify:全局函数height=verify(var);
}请注意,全局函数verify的声明在Screen类的定义之前是不可见的。然而,名字查找的第三步包括了成员函数出现之前的全局作用域。在此例中,verify的声明位于 setHeight的定义之前,因此可以被正常使用。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在 VS Code 中快速使用 Copilot 来辅助开发

在日常开发中,编写代码往往是最耗时的环节之一。而 GitHub Copilot,作为一款 AI 编码助手,可以帮助开发者 自动补全代码、生成代码片段,甚至直接编写完整的函数,大幅提升编码效率。那么,如何在 VS Code 中快…

剑指 Offer II 024. 反转链表

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20024.%20%E5%8F%8D%E8%BD%AC%E9%93%BE%E8%A1%A8/README.md 剑指 Offer II 024. 反转链表 题目描述 给定单链表的头节点 head ,请反转链表&#xff…

通过API 调用本地部署 deepseek-r1 模型

如何本地部署 deepseek 请参考(windows 部署安装 大模型 DeepSeek-R1) 那么实际使用中需要开启API模式,这样可以无拘无束地通过API集成的方式,集成到各种第三方系统和应用当中。 上遍文章是基于Ollama框架运行了deepSeek R1模型…

【产品经理】需求分析方法论+实践

阐述了需求分析的基本认知,包括需求分析的定义、原则和内容。接着,文章详细介绍了需求分析的十个步骤,从收集需求到结果评审,为产品经理提供了清晰的操作指南。 作为产品经理,需求分析是一个最基本的工作,但…

【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)

《API Testing and Development with Postman》最新第二版封面 文章目录 最新版《Postman 接口测试与开发实战》示例 API 项目本地部署保姆级搭建教程1 前言2 准备工作3 具体部署3.1 将项目 Fork 到自己名下3.2 创建虚拟环境并安装依赖3.3 初始运行与项目调试 4 示例项目的用法…

2025年02月19日Github流行趋势

项目名称:OmniParser 项目地址url:https://github.com/microsoft/OmniParser 项目语言:Jupyter Notebook 历史star数:12878 今日star数:2153 项目维护者:yadong-lu, ThomasDh-C, aliencaocao, nmstoker, kr…

侯捷 C++ 课程学习笔记:设计模式在面向对象开发中的应用

在侯捷老师的《C 面向对象开发》课程中,除了对面向对象编程的基础特性(封装、继承和多态)的深入讲解外,还引入了设计模式这一高级主题。设计模式是面向对象编程中的一种最佳实践,能够帮助开发者解决常见的设计问题&…

前七章综合练习

一,拓扑图 二,实验要求 不限 三,实验步骤 第一步,搭建拓扑图 如上 注意: 第二步,配置IP trust: client1 client2 fw untrusrt-1: fw r3 电信DNS 百度web-1 untrust-2&#xf…

个人shell脚本分享

在周一到周五做增量备份,在周六周日做完全备份 #!/bin/bash定义变量 SRC“/path/to/source” # 源目录 BKUP“/backup” # 备份主目录 FUL“KaTeX parse error: Expected EOF, got # at position 22: …ull" #̲ 完全备份目录 INC"BKUP/inc” # 增量备份…

C语言之函数封装技巧

目录 前言 一、函数在源代码中的三种状态 二、函数封装的运用 案例1&#xff1a;实现打印20以内的素数 案例2&#xff1a;存放因子数并返回长度 三、return返回与形参返回 四、<>与“” 五、解耦 总结 前言 在C语言中&#xff0c;函数封装是一种重要的技巧&#…

深度神经网络终极指南:从数学本质到工业级实现(附Keras版本代码)

深度神经网络终极指南&#xff1a;从数学本质到工业级实现&#xff08;附Keras版本代码&#xff09; 为什么深度学习需要重新理解&#xff1f;&#xff08;与浅层模型的本质差异&#xff09; 模型类型参数容量特征学习方式适合问题类型浅层模型102-104手动特征工程低维结构化数…

vue3 + thinkphp 接入 七牛云 DeepSeek-R1/V3 流式调用和非流式调用

示例 如何获取七牛云 Token API 密钥 https://eastern-squash-d44.notion.site/Token-API-1932c3f43aee80fa8bfafeb25f1163d8 后端 // 七牛云 DeepSeek API 地址private $deepseekUrl https://api.qnaigc.com/v1/chat/completions;private $deepseekKey 秘钥;// 流式调用pub…

IIS asp.net权限不足

检查应用程序池的权限 IIS 应用程序池默认使用一个低权限账户&#xff08;如 IIS_IUSRS&#xff09;&#xff0c;这可能导致无法删除某些文件或目录。可以通过以下方式提升权限&#xff1a; 方法 1&#xff1a;修改应用程序池的标识 打开 IIS 管理器。 在左侧导航树中&#x…

代码解读:如何将HunYuan T2V模型训练成I2V模型?

Diffusion models代码解读:入门与实战 前言:HunYuan T2V模型出来很久了,但是想要训练成I2V的模型还是有点难度。此外,还有很多预训练视频模型都是T2V的,可以借鉴本文的方法加入参考图作为条件,并严格保持视频的第一帧与Image一样。 目录 Patch Image Padding Channel …

windows事件倒计时器与提醒组件

widgets 这是桌面组件前端开源组件&#xff0c;作者称&#xff1a;项目还在持续完善中&#xff0c;目前包含键盘演示、抖音热榜、喝水提醒、生日列表、待办事项、倒计时、灵动通知、打工进度等多个组件 有vue编程能力的可以自己做组件 百度网盘 夸克网盘 桌面组件 | Ca…

汽车零部件工厂如何通过工业一体机实现精准控制

在汽车制造行业中&#xff0c;零部件的精度和质量直接关系到整车的性能与安全。随着汽车工业的快速发展&#xff0c;汽车零部件工厂对生产过程的精准控制提出了更高的要求。传统的生产管理模式往往依赖人工操作和分散的系统&#xff0c;难以满足现代汽车零部件工厂的需求。而工…

BMS保护板测试仪:电池安全与性能的坚实守护者

在新能源汽车、储能系统、电动工具等电池驱动型产品日益普及的今天&#xff0c;电池的安全性和性能成为了人们关注的焦点。而BMS保护板测试仪作为电池管理系统&#xff08;BMS&#xff09;中不可或缺的一部分&#xff0c;为电池的安全运行提供了有力保障。 BMS保护板测试仪的重…

Django的初步使用

1.安装Django pip install django 验证是否安装成功&#xff1a; $ python3 Python 3.8.10 (default, Jan 17 2025, 14:40:23) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >…

(前端基础)CSS(一)

了解 Cascading Style Sheet&#xff1a;层叠级联样式表 CSS&#xff1a;表现层&#xff08;美化网页&#xff09;如&#xff1a;字体、颜色、边框、高度、宽度、背景图片、网页定位、网页浮动 css优势&#xff1a; 内容和表现分离网页结构表现统一&#xff0c;可以实现复用…

CASAIM与韩国DOOSAN集团达成合作,开启工业制造自动化检测新篇

近日&#xff0c;CASAIM与韩国知名跨国企业斗山集团&#xff08;DOOSAN&#xff09;达成战略合作&#xff0c;联合打造CASAIM全自动化智能检测系统&#xff0c;助力斗山集团全面提升产品质量检测精度与效率&#xff0c;完成智能化检测升级&#xff0c;保持在全球市场竞争中的领…