C++中的局部静态(static)

1. 概述

在前面,我们了解了static关键字在特定上下文中的含义,今天我们看看另一个环境,你可能会在这里找到static关键字,这是一个局部作用域。我们可以在局部作用域中使用static来声明一个变量,这和我们之前看到的2种static有点不同。这次的局部静态(local static)会有更多的含义。

声明一个变量,我们需要考虑两种情况。那就是变量的生存周期变量的作用域

生存周期指的是变量实际存在的时间,也就是说,在它被删除之前,它会在我们的内存中存在多久。

变量作用域指的是我们可以访问到变量的范围。

所以,如果在一个函数内部声明一个变量,我们不能在其他函数中访问它,因为我们声明的变量对于我们声明的函数是局部的。

静态局部(local static)变量允许我们声明一个变量,它的生存期基本上相当于整个程序的生存期,然而,它的作用范围被限制在这个函数内,但它其实和函数没有什么关系,我的意思是,我们可以在任何作用域中声明这个,刚才只是用用函数举个例子,这不仅仅局限于函数内部,也可以在if语句中,也可以在任何位置。这就是为什么函数作用域中static和类作用域中的static没有太大的区别的原因。因为生存期实际上是相同的。唯一的区别是,在类作用域中,类中的任何东西都可以访问这个静态变量。然而如果你在函数作用域中声明一个静态变量,那么它将是那个函数的局部变量。对类来说也是局部变量

2. 案例

1. 项目准备

我们有一个简单的项目,项目中有main.cpp文件,内容如下

2. 开始案例

最简单的例子就是创建一个函数,然后再其中声明一些静态变量

这意味着,当我第一次调用函数时,函数的静态变量i将被初始化为0,然后所有对函数的后续调用,实际上不会创建一个全新的变量。

检验这个的简单方法是,首先,我们去掉变量i的static修饰,然后每次调用函数时都增加i的值,然后我们打印i的值,如下

这个函数所期望的是每次我们调用这个函数时,i被设置为0,然后i++,i自增1,然后将i打印到控制台。

我们通过调用函数来进行测试,这里我们会调用它5次

F5运行程序

可以看到1被打印了5次。正如我们刚才解释的,我们每次调用函数都创建了一个变量i,我们令它等于0,然后自增1,最后打印到控制台。

现在,我们将函数中定义的变量用static修饰,使其成为局部静态变量

这非常类似于

F5运行程序

这种方法的问题是,我们可以在任意地方访问这个变量i,比如我们在Function调用之间令i等于10,如下

我们按下F5运行程序

这会极大的改变了我们程序所有的事情。

如果我们想做这些,但又不想让每个人都能访问这个变量,我们可以在局部作用域下声明成static。如下

这意味着,我们第一次运行这个函数时,首先创建变量i,并设为0,然后,后续进来就不会再执行这个初始化的过程。

我们F5运行程序

和在函数外定义变量i的结果是一样的。区别是,在函数外定义变量i在全局范围内可见,而这在函数内定义局部静态变量,针对本函数。至于使用,有些人不赞成使用这种方法,因为我不完全理解其中的原因,因为我不认为这有什么问题,它确实有它的用处。这就是其中之一。我们可以使用其他方法实现完全相同的行为,比如可以用类来实现,但我们根本不必使用类来编写程序。局部静态确实让这更加容易,可以让我们的代码更加干净。

另一个例子是,如果我们有一个单例类Singleton,因为是单例类,所以只存在一个实例。如果我们想创建这个单例类而不使用静态局部作用域,我就需要创建静态的单例实例,可能是一个指针static Singleton* s_Instance。如果我们想返回一个引用,我们必须有一个返回Singleton&的Get函数,它是静态的,然后返回我们的实例,当然返回要逆向引用*s_Singleton static Singleton& Get() { return *s_Singleton; }

然后我们需要声明这个实例,我们可以把它默认设为空指针。

删掉Function相关的代码,这里用不到了。

我们可以在main函数中调用Singleton::Get,然后对它做任何我们想做的事情。

假设我们的单例中有一个方法叫Hello,里面内容不重要,这只是一个例子。

我们可以通过单例调用Hello方法。

可以看到,我们得到了一个可以使用的类实例,我们可以使用静态方式来使用它。这样的代码有很多,我们不一定要这样做,另一种方式是使用我们的新知识local static。

代码可能是这样的

我们得到了完全相同的行为,你可以看到,调用一切都保持不变,我们可以运行代码,没有问题。

F5运行程序

当然,现在这代码没有做任何事情。但可以看到它成功运行,没有崩溃。

可以看到,我们的代码现在干净多了。

当然,如果,Get方法里的instance变量没有static关键字修饰

那么,这个单例会在栈上创建,当代码运行到Get方法的结束大括号时,函数作用域结束,这个instance变量就会被销毁,所以这是一个严重的错误,特别是,还返回的引用

如果只是复制值,当然没有问题

然而,因为我们返回一个对它的引用

那就是大问题了。

然而,通过添加静态,将它的生存期延长到永远。

这意味着,我们第一次调用Get,它实际上会构造一个单例实例,在接下来的调用,它只会返回这个已经存在的实例。所以这是一个很好的例子,当我们像这样用的时候。不一定非要用单例类,我们可以通过替换掉初始化函数即可,例如,我们可能需要在程序中的某处调用一个静态初始化函数来创建所有对象,那我们就可以使用静态Get方法之类的东西。我们可以在很多情况下用它来简化代码。可以看到它有很多用途,其实也没有那么糟糕,所以请方形使用它。

相关推荐
闻缺陷则喜何志丹11 分钟前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
charlie11451419122 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
小林熬夜学编程34 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
倔强的石头1061 小时前
【C++指南】类和对象(九):内部类
开发语言·c++
A懿轩A2 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导2 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香2 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3053 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻3 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工3 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++