当前位置: 首页 > news >正文

「C++系列」C++ 变量作用域

文章目录

  • 一、C++ 变量作用域
  • 二、局部变量
  • 三、全局变量
  • 四、类作用域
  • 五、相关链接

在这里插入图片描述

一、C++ 变量作用域

在C++中,变量的作用域(Scope)指的是变量在程序中可以被访问的区域。作用域由花括号{}定义,这些花括号可以出现在函数体、控制结构(如if语句、循环语句)或任何块级结构中。根据变量的定义位置,C++中的变量可以分为几种不同的作用域类型:

  1. 全局作用域(Global Scope)
  • 在所有函数外部定义的变量具有全局作用域。
  • 这些变量在程序的整个执行期间都是可见的,并且可以从程序的任何位置访问。
  • 尽量避免使用全局变量,因为它们可能导致代码难以理解和维护。
  1. 局部作用域(Local Scope)
  • 在函数内部或块(由花括号{}包围的代码块)内部定义的变量具有局部作用域。
  • 这些变量仅在其定义的函数或块内部可见和可访问。
  • 一旦离开定义它们的函数或块,这些变量将不再存在。
  1. 命名空间作用域(Namespace Scope)
  • 在命名空间中定义的变量具有命名空间作用域。
  • 这些变量在该命名空间内是可见的,但可以通过使用命名空间的名称来从外部访问。
  • 命名空间用于组织代码,避免命名冲突。
  1. 类作用域(Class Scope)
  • 在类内部定义的变量(成员变量)具有类作用域。
  • 这些变量只能通过类的对象或类的成员函数来访问。
  • 根据访问权限(public、protected、private),这些变量的可见性可以进一步限制。
  1. 块作用域(Block Scope)
  • 实际上,局部作用域和块作用域可以视为同一概念的不同表述。
  • 任何由花括号{}包围的代码块内部定义的变量都具有块作用域。
  1. 函数原型作用域(Function Prototype Scope)
  • 在函数原型中声明的参数名仅在函数原型内部具有作用域。
  • 这些名称在函数定义或函数体内部是不可见的。

理解变量的作用域对于编写高效、可维护的C++代码至关重要。正确地使用作用域可以避免命名冲突,提高代码的可读性和可维护性。同时,也要注意作用域的生命周期,确保在适当的时候释放不再需要的资源,以避免内存泄漏等问题。

二、局部变量

C++中的局部变量是在函数内部或代码块(由花括号{}包围的区域)中定义的变量。这些变量仅在定义它们的函数或代码块内部可见和可访问,一旦离开该作用域,这些变量就会被销毁,其占用的内存也会被释放(对于自动存储期的局部变量而言)。

下面案例,展示了局部变量的使用:

#include <iostream>// 函数声明
void printNumbers();int main() {// main函数中的局部变量int mainVar = 10;std::cout << "在main函数中,mainVar的值为: " << mainVar << std::endl;// 调用函数printNumbers();// 注意:这里不能访问printNumbers函数中的局部变量,因为它们的作用域仅限于该函数内部return 0;
}// 函数定义
void printNumbers() {// printNumbers函数中的局部变量int num1 = 5, num2 = 10;// 另一个代码块(if语句内部)if (num1 < num2) {// 这个变量仅在if语句的代码块内部有效int temp = num1 + num2;std::cout << "num1 + num2 = " << temp << std::endl;// 尝试在if语句外部访问temp会导致编译错误// std::cout << "temp的值为: " << temp << std::endl; // 错误:temp在此处不可见}// 在这里可以访问num1和num2,但不能访问tempstd::cout << "num1的值为: " << num1 << ", num2的值为: " << num2 << std::endl;// 函数结束,num1、num2和temp(如果它能在函数外部访问的话,但实际上不能)都将被销毁
}

在这个例子中,mainVarmain函数中的局部变量,而num1num2tempprintNumbers函数中的局部变量。temp变量是在if语句的代码块中定义的,因此它仅在该代码块内部可见和可访问。一旦执行离开if语句的代码块,temp变量就会被销毁,其占用的内存也会被释放。

注意,虽然局部变量在函数或代码块结束时会被销毁,但它们的销毁并不直接等同于内存的立即释放。内存的释放依赖于C++的运行时环境(如栈的弹出操作),这通常发生在函数返回或代码块执行完毕后。然而,对于动态分配的内存(使用new操作符分配的内存),则需要在适当的时候使用delete操作符来显式释放,以避免内存泄漏。局部变量通常不涉及动态内存分配,因此不需要程序员显式地释放它们占用的内存。

三、全局变量

在C++中,全局变量是在所有函数外部定义的变量,这意味着它们在程序的整个执行期间都是可见的,并且可以从程序的任何位置访问。但是,过度使用全局变量可能会导致代码难以理解和维护,因为它们可以在程序的任何地方被修改,从而引入难以追踪的错误。

下面案例,展示了全局变量的使用:

#include <iostream>// 全局变量定义
int globalVar = 100;// 函数声明
void modifyGlobalVar();
void printGlobalVar();int main() {// 在main函数中访问全局变量std::cout << "在main函数中,全局变量globalVar的初始值为: " << globalVar << std::endl;// 调用函数修改全局变量modifyGlobalVar();// 再次在main函数中访问全局变量,查看其值是否已改变std::cout << "在modifyGlobalVar函数执行后,全局变量globalVar的值为: " << globalVar << std::endl;// 调用另一个函数打印全局变量的值printGlobalVar();return 0;
}// 函数定义:修改全局变量的值
void modifyGlobalVar() {// 直接访问并修改全局变量globalVar = 200;std::cout << "在modifyGlobalVar函数中,全局变量globalVar的值已被修改为: " << globalVar << std::endl;
}// 函数定义:打印全局变量的值
void printGlobalVar() {// 访问并打印全局变量的值std::cout << "在printGlobalVar函数中,全局变量globalVar的值为: " << globalVar << std::endl;
}

在这个例子中,globalVar是一个全局变量,它在main函数、modifyGlobalVar函数和printGlobalVar函数中都是可见的。modifyGlobalVar函数修改了globalVar的值,然后在main函数中通过两次打印来展示这个变化。printGlobalVar函数也打印了globalVar的值,以进一步证明全局变量的全局可见性。

然而,尽管全局变量在某些情况下可能很方便,但通常建议尽可能避免使用它们,而是使用函数参数、返回值或类成员变量来传递和存储数据。这样可以提高代码的可读性、可维护性和可重用性。

四、类作用域

在C++中,类作用域(Class Scope)是指类内部定义的成员(包括成员变量和成员函数)的作用域。这些成员只能通过类的对象或者类的成员函数来访问(取决于成员的访问权限:public、protected或private)。

下面是一个C++类作用域的案例代码:

#include <iostream>
#include <string>// 定义一个名为Person的类
class Person {
private: // 私有成员,只能通过成员函数访问std::string name; // 私有成员变量int age; // 另一个私有成员变量public: // 公有成员函数,可以在类外部通过对象访问// 构造函数,用于初始化对象Person(std::string n, int a) : name(n), age(a) {}// 成员函数,用于设置名字void setName(std::string n) {name = n;}// 成员函数,用于获取名字std::string getName() const {return name;}// 成员函数,用于设置年龄void setAge(int a) {if (a >= 0) {age = a;}}// 成员函数,用于获取年龄int getAge() const {return age;}// 成员函数,用于打印个人信息void printInfo() const {std::cout << "Name: " << name << ", Age: " << age << std::endl;}
};int main() {// 创建Person类的对象Person person1("Alice", 30);// 通过公有成员函数访问私有成员std::cout << "Initial info of person1: ";person1.printInfo();// 修改对象的属性person1.setName("Bob");person1.setAge(25);// 再次打印以查看修改后的信息std::cout << "Modified info of person1: ";person1.printInfo();// 创建另一个Person类的对象Person person2;// 注意:这里person2使用了默认构造函数(如果未定义,则编译器会生成一个),因此其成员变量可能未被初始化// 最好为所有类定义至少一个构造函数来初始化成员变量return 0;
}

在这个例子中,Person类有两个私有成员变量nameage,以及多个公有成员函数来访问和修改这些私有成员变量的值。注意,私有成员变量nameage不能直接从类外部访问,只能通过类的公有成员函数(如getNamesetNamegetAgesetAge)来访问和修改它们的值。

此外,Person类还定义了一个构造函数,用于在创建Person类的对象时初始化nameage成员变量。构造函数是一个特殊的成员函数,它在创建类的对象时自动调用。

最后,main函数中创建了两个Person类的对象person1person2,并通过调用Person类的公有成员函数来访问和修改这些对象的私有成员变量的值。注意,由于person2在创建时没有提供初始化参数,并且我们没有为Person类定义默认构造函数来初始化其成员变量,因此person2的成员变量可能处于未定义状态(这取决于编译器如何处理未初始化的局部变量)。在实际应用中,最好为所有类定义至少一个构造函数来初始化其成员变量。

五、相关链接

  1. Visual Studio Code下载地址
  2. Sublime Text下载地址
  3. 「C++系列」C++简介、应用领域
  4. 「C++系列」C++ 基本语法
  5. 「C++系列」C++ 数据类型
  6. 「C++系列」C++ 变量类型

相关文章:

  • 谈谈检测浏览器类型
  • Jenkins 使用 Publish over SSH进行远程访问
  • p标签文本段落中因编辑器换行引起的空格问题完美解决方案
  • 【Element-UI】vue使用 this.$confirm区分取消与关闭,vue给this.$confirm设置多个按钮
  • WHAT - React Immer
  • QT学习(6)——QT中的定时器事件,两种实现方式;事件的分发event,事件过滤器
  • 【软件工程】计算机内存单位解析及换算
  • vue3中svg图标的封装与使用
  • 万字 | 菊花厂C语言编程10大规范
  • Python基础小知识问答系列-过滤列表元素
  • python 基础综合应用——小开发
  • Swift 定制 Core Data 迁移
  • JavaScript 原型链那些事
  • 3D鸡哥又上开源项目!单图即可生成,在线可玩
  • Spring Boot 学习第八天:AOP代理机制对性能的影响
  • JS 中的深拷贝与浅拷贝
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • chrome扩展demo1-小时钟
  • rabbitmq延迟消息示例
  • Xmanager 远程桌面 CentOS 7
  • 爬虫模拟登陆 SegmentFault
  • 微信小程序设置上一页数据
  • ​​​​​​​​​​​​​​Γ函数
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • # Maven错误Error executing Maven
  • #07【面试问题整理】嵌入式软件工程师
  • #vue3 实现前端下载excel文件模板功能
  • $$$$GB2312-80区位编码表$$$$
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (九十四)函数和二维数组
  • (七)c52学习之旅-中断
  • (转)大型网站的系统架构
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .Net 高效开发之不可错过的实用工具
  • .NET 药厂业务系统 CPU爆高分析
  • .Net(C#)自定义WinForm控件之小结篇
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .Net各种迷惑命名解释
  • [ Linux 长征路第五篇 ] make/Makefile Linux项目自动化创建工具
  • [.NET 即时通信SignalR] 认识SignalR (一)
  • [2016.7 day.5] T2
  • [C#]C#学习笔记-CIL和动态程序集
  • [c#基础]DataTable的Select方法
  • [C++]模板与STL简介
  • [C++提高编程](三):STL初识
  • [Delphi]一个功能完备的国密SM4类(TSM4)[20230329更新]
  • [docker]docker网络-直接路由模式
  • [flume$2]记录一个写自定义Flume拦截器遇到的错误
  • [ios] IOS文件操作的两种方式:NSFileManager操作和流操作【转】
  • [k8s系列]:kubernetes·概念入门