017 - C++ 中的静态(static)

news2024/9/21 14:04:52

本期我们将讨论 C++ 中的 static(静态)。

static 关键字在 C++ 中有两个意思,这个取决于上下文。

简单了解

第一种情况是在类或结构体外部使用 static 关键字,另一种是在类或者结构体内部使用 static。

基本上我们可以这样理解,类外面的 static 意味着你声明为 static 的内容,将只是在内部链接,这意味着它只能对它所在的转换单元可见。

然而,类或结构体内部的静态变量 static 意味着该变量将与类的所有实例共享内存,这意味着该静态变量在你的类创建的所有实例中,静态变量只有一个实例。类似的事情也适用于类中的静态方法。

本期不深入讨论静态 static 在类或结构体范围内的实际含义,在之后的系列中我们会介绍。只关注在类和结构体外部的 static。

让我们通过一些代码来看一下。

i6RWst.png

我在一个完全空的 C++ 文件中定义了一个静态变量,使用惯例使用 S_ 来表示这个变量是静态的,将其值设置为 5。它整体上看起来和其他变量是一样的,只不过在它前面的是 static 关键字,这意味着这个变量只会在当前这个转换单元内部链接。

如果你还不知道 C++ 编译和链接是如何工作的,可以回去看一看 C++ 编译和链接的那一期,因为你真的需要了解发生了什么,然后才能学习本期的内容。

静态变量或函数意味着当需要将这些函数或变量与实际定义的符号链接时,链接器不会在这个转换单元的作用域之外的地方寻找那个符号定义。

静态变量

我们拿实际的代码来解释一下。

i6RtcX.png

在第一个例子中,我们创建了一个静态变量,并将它设为 5,然后去另一个 C++ 文件,也就是另一个转换单元,一个带有main函数空白 C++ 文件。在这里创建一个全局变量。将它与之前静态变量设置一样的名字。其值设置为十,打印这个变量。

运行之后,我们可以看到,程序是可以编译的,不会遇到任何问题。

然后我们回到 Static.cpp 文件,删除了static 关键字。再次编译刚刚的代码。你会发现当它进入链接状态阶段时,会报一个链接错误。因为这个 s_Variable 变量已经在另一个转换单元中定义过了,当然是这样的,我们不能有两个同名的全局变量。

i6R0fJ.png

那么只有这一种调整方法了吗?当然不是。

我们可以这样修改。

i6RC8c.png

这样操作意味着它会在外部转换单元中寻找 s_Variable 变量。这被称为 external linkage 或者 external linking。

现在运行代码也是没有问题了的。它可以输出 5,它引用了另外一个文件中的 s_Variable 变量的值。

这个时候如果我再次将另外一个文件中的 s_Variable 标记为静态。——这个过程有点像在类中声明一个私有变量,链接器在全局作用域下将不会看到这个变量。程序它还是会报错,因为我们已经做了有效的标记,这个变量已经是私有的了。

静态函数

接下来我们试一下函数 function。

i6RlGA.png

i6RwYo.png

我在 Static.cpp 文件中定义一个 function函 数。然后在 Main.cpp 中也定义一个具有相同签名的函数,返回值也是void。

编译一下这个程序。我们在链接阶段同样得到一个重复的符号错误。

回到 Static.cpp 文件中,使用 static 将它标记为静态的。链接器开始工作时,根本不会看到这个静态的函数。程序会顺利编译,不会得到任何错误。

i6RHwN.png

这就是 C++ 中静态的全部含义。

当你在类和结构体之外使用静态时,它只是意味着你声明的静态函数或静态变量只会在它所在的文件中被看到。

如果你想在头文件中声明一个静态变量,将头文件包含在两个不同的 C++ 文件中。其实和上面的例子是一样的,都是在两个转换单元中都声明了相同的 s_Variable 变量为静态变量。当然,当你包含那个头文件时,它会复制所有内容并将其粘贴在 C++ 文件中。也就是将一个静态变量放到两个不同的转换单元中,这个你要留意一下。

最后的话

那么我们使用 static 的理由是什么呢?

你可以参考一下我们为什么要在类中使用 private?

如果你不需要变量是全局变量,你就需要尽可能多的使用静态变量。一旦在全局作用域下声明了东西,如果没有设定为static,链接器将会跨编译单元进行链接。这意味着你已经创建了一个完全全局的变量,它可以在任何地方使用,这可能会导致一些非常糟糕的 bug。

归根到底,全局变量是不好的。我不太喜欢用全局变量,也不建议你使用。除非你真的需要他们跨转换单元链接。

好了,本期的内容就是这些,下期再见。

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

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

相关文章

背包问题基础与应用

背包问题 理论基础 01背包 背包中的每个物品只能用一次 物品编号重量价值物品1115物品2320物品3430 定义:dp[i][j]表示从下标0-i的物品中任取,放进容量为j的背包的最大价值 初始化: dp [[0] * (bag_size 1) for _ in range(len(weigh…

Java程序内存占用优化实践

目录背景堆内存调整内存还会继续上涨减少线程数量TomcatDubboLogback野线程背景 上了微服务的当,喜欢将服务各种拆分,公有云模式下服务器比较多,还能玩得转。到了私有化部署,有的客户连个技术人员都没有,只想一键启动…

java——《面试题——基础篇》

1、 Java语言有哪些特点 1、简单易学、有丰富的类库 2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高) 3、与平台无关性(JVM是Java跨平台使用的根本) 4、可靠安全 5、支持多线程 2、面向对象…

Baumer工业相机堡盟工业相机如何联合BGAPI SDK和OpenCVSharp实现Mono12和Mono16位深度的图像保存(C#)

Baumer工业相机堡盟工业相机如何联合BGAPI SDK和OpenCVSharp实现Mono12和Mono16位深度的图像保存(C#) Baumer工业相机Baumer工业相机保存位深度12/16位图像的技术背景代码案例分享1:引用合适的类文件2:BGAPI SDK在图像回调中联合O…

【GCU体验】基于PaddlePaddle + GCU跑通模型并测试GCU性能

一、环境 地址:启智社区:https://openi.pcl.ac.cn/ 二、计算卡介绍 云燧T20是基于邃思2.0芯片打造的面向数据中心的第二代人工智能训练加速卡,具有模型覆盖面广、性能强、软件生态开放等特点,可支持多种人工智能训练场景。同时具备灵活的可…

STM32理论 —— 定时器、时钟

文章目录 1. 定时器1.1 分类与简介1.1.1 分类与主要功能特点1.1.2 三种常用的定时器简介1.1.3 三种计数模式1.1.4 定时器计数原理 1.2 时钟来源1.3 通用定时器简介1.4 计数溢出时间公式1.4 定时器中断的原理1.5 输入捕获1.6 核心代码1.6.1 通用定时器初始化1.6.2 高级定时器初始…

【Python_Scrapy学习笔记(十三)】基于Scrapy框架的图片管道实现图片抓取

基于Scrapy框架的图片管道实现图片抓取 前言 本文中介绍 如何基于 Scrapy 框架的图片管道实现图片抓取,并以抓取 360 图片为例进行展示。 正文 1、Scrapy框架抓取图片原理 利用 Scrapy 框架提供的图片管道类 ImagesPipeline 抓取页面图片,在使用时需…

领域驱动设计理论实践

战略设计 战略设计是将“混沌”解构成“清晰”的过程,在该过程从开始到结束的历程之中,我们会划分出领域、界定通用语言范围、确定出系统限界上下文以及上下文之间的映射方式。 领域划分 战略设计在领域驱动设计中起着关键作用,因为其决定了…

使用Bazel构建前端Sass

注:本文假设对Bazel有一定的了解。本文基于Bazel 4.2.2 版本 在web前端领域,前端样式,web浏览器只认CSS样式语言。而CSS样式语言又过于低级。于是有人发明了更高级的语言:Sass[1],用于生成CSS代码。 这样的方案&#x…

【C++】队列模拟问题

文章目录队列模拟问题12.7.1 ATM问题12.7.2 队列类12.7.3 Queue类的接口12.7.4 **Queue类的实现**12.7.5 是否需要其他函数?12.7.6 Customer类queue.hqueue.cpp12.7.7 ATM模拟main.cpp队列模拟问题 12.7.1 ATM问题 Heather银行打算在Food Heap超市开设一个自动柜员…

【C++STL精讲】vector的基本使用与常用接口

文章目录💐专栏导读💐文章导读🌷vector是什么?🌷vector的基本使用🌷vector常用函数接口💐专栏导读 🌸作者简介:花想云,在读本科生一枚,致力于 C/C…

HAL库版FreeRTOS(上)

目录 FreeRTOS 简介初识FreeRTOS什么是FreeRTOS?为什么选择FreeRTOS?FreeRTOS 的特点商业许可 磨刀不误砍柴工查找资料FreeRTOS 官方文档Cortex-M 架构资料 FreeRTOS 源码初探FreeRTOS 源码下载FreeRTOS 文件预览 FreeRTOS 移植FreeRTOS 移植移植前准备添加FreeRTO…

浏览器断点调试说明

断点调试 断点调试面板 功能按钮介绍 描述:继续执行脚本 或者叫(逐过程执行) 快捷键 (F8)或者是(Ctrl\) 作用:打断点了的地方(比如有是三个断点地方)就会 第一…

大数据能力提升项目|学生成果展系列之四

导读 为了发挥清华大学多学科优势,搭建跨学科交叉融合平台,创新跨学科交叉培养模式,培养具有大数据思维和应用创新的“π”型人才,由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

13.vue-cli

单页面应用程序:所有的功能只在index.html中完成 vue-cli是vue版的webpack 目录 1 安装vue-cli 2 创建项目 3 使用预设 4 删除预设 5 开启项目 6 项目文件内容 6.1 node_moduls 中是项目依赖的库 6.2 public 6.2.1 favicon.ico 是浏览器页签内部…

尚融宝——整合OpenFeign与Sentinel实现兜底方法——验证手机号码是否注册功能

一、整合过程 在项目添加依赖&#xff1a;添加位置 <!--服务调用--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency> 在需要的服务中添加启动注…

spring中常见的注解

DI(依赖注入中常见的注解) Autowired&#xff1a;按类型自动装配Resource&#xff1a;按名称或类型自动装配&#xff0c;Qualifier&#xff1a;按名称自动装配&#xff0c;Value &#xff1a;注入int、float、String等基本数据类型&#xff0c;只能标注在成员变量、setter方法上…

【Gradle-1】入门Gradle,前置必读

1、为什么要学习Gradle Gradle作为Android开发默认的构建工具&#xff0c;你的每一次编译都会用到它。招聘要求从以前的熟悉加分&#xff0c;到现在的必备技能&#xff0c;可见Gradle的重要性。 做开发这么久了&#xff0c;你是否对Gradle又爱又恨&#xff1f;是否对Gradle的…

第三章(1):自然语言处理概述:应用、历史和未来

第三章&#xff08;1&#xff09;&#xff1a;自然语言处理概述&#xff1a;应用、历史和未来 目录第三章&#xff08;1&#xff09;&#xff1a;自然语言处理概述&#xff1a;应用、历史和未来1. 自然语言处理概述&#xff1a;应用、历史和未来1.1 主要应用1.2 历史1.3 NLP的新…

【科普】PCB为什么常用50Ω阻抗?6大原因

在PCB设计中&#xff0c;阻抗通常是指传输线的特性阻抗&#xff0c;这是电磁波在导线中传输时的特性阻抗&#xff0c;与导线的几何形状、介质材料和导线周围环境等因素有关。 对于一般的高速数字信号传输和RF电路&#xff0c;50Ω是一个常用的阻抗值。 为什么是50Ω&#xff1f…