首页 > 其他分享 >静态变量

静态变量

时间:2023-11-26 16:57:01浏览次数:36  
标签:变量 静态 int Say static 全局变量

代表「静态」的关键字是 static ,它拥有多种含义。

  1. 用于声明某个变量是持久存储的(类似于全局变量)。
  2. 用于限制某个变量/函数不能从其他文件中访问。
  3. 用于声明一个成员变量/函数是一个全局变量/函数(所有对象共有)。

接下来我们分别对这几种情况来做讨论。

用于持久存储的声明与全局变量

  • 将一个本地变量声明为 static 类型以达到持久存储的目的。

实际上,静态本地变量就是全局变量,它与全局变量的使用方法相似,它们都在链接的时候被分配了内存空间,它们的作用域都是从它们的定义到 程序结束。

唯一不同的是初始化的时间——全局变量是在 main() 函数执行之前,静态变量是在程序运行到定义它之时。

class X 
{
public:
  X(int a, int b);
  ~X();
};

void f()
{
  static X my_X(10, 20);
}

譬如上面这段代码,当程序运行到 static X my_X(10, 20); 一句的时候,my_X 才会被初始化。当 f 之后再次被调用时,不会重复初始化对象 my_X,也就是说,当一个静态变量/对象被初始化之后,它就从各种意义上成为了一个全局变量。

Q:我们如何做到记住某个对象已经被初始化了而不至于在重复调用的函数中不再重复初始化它呢?
A:最简单的一种方案就是创建一个隐藏起来的标志来证明某个对象已经被初始化过了,实际上,C++ 上也确实是这么做的,这里不再展开讲。

需要注意的是:多文件程序的各个文件中的全局变量初始化顺序是不一定的——即使是同一个编译器的两次编译运行之间都有可能出现差异。这提醒我们,要特别注意初始化时的依赖关系。

解决办法只有两个:要么不做这件事,要么就把所有的全局变量放到同一个文件里。

用于限制单文件访问

  • static 修饰的变量只能在定义它的文件中访问,而不能在其他文件中访问(即使它在其他文件中做过声明)。
  • 需要注意的是,使用 static 来限制访问权限的做法在 C++ 中已经被弃用了,因为我们有更好的方法(比如 class 中的 public,private,protected 等关键字)来做这件事。

例如下面这个程序会编译错误:

/* in flie1.cpp */

#include <iostream>

using namespace std;

extern int gGlobal;
extern int sLocal;
//声明两个变量

int f();
//声明 f 函数,因为 f 函数的实体在 file2 中,所以 f 可以正常访问 file2 中的 static 变量 sLocal
int g()
{
  return gGlobal + sLocal;
}
//定义 g 函数,g 不能访问 file2 中的 static 变量,报错

int main()
{
  cout << f() << ' ' << g() << endl;
}

/* in file2.cpp */

int gGlobal = 10;
//gGlobal 是所有文件共享的
static int sLocal = 20;
//sLocal 只能在 file2 中访问

int f()
{
  return gGlobal + sLocal;
}

报错如下,显示声明变量 sLocal 但是未定义。

在上面这段代码中,sLocal 变量是一个 static 变量,它只能在 file2 中访问。即使 file1 中声明了它,仍然是不可以在 file1 中调用的。

静态成员

关于静态成员/函数我很早之前总结过一次,但是不太准确,这里再总结一次。

静态成员变量

前面第一个大标题下我们说明了 static 修饰的变量可以视为全局变量。

如果类里面有一个 static 修饰的成员,那么这个成员实际上就不存在于任何一个成员里了,而是归属于这个类的一个全局变量。

class A
{
public:
  A() : i(0), j(0) {}
  ~A(){}
  void Set(int ii){ i = ii; }
  static int i;
  int j;
}

int main()
{
  A::i = 10;
}

这段代码是不能运行的。

  • 在很久之前说过,类中的变量全部都是声明而不是定义。它们直到属于这个类的一个对象被定义,才算有了定义,并且调用它们时必须指定是「哪个对象的变量」。
    例如:A::j = 10; 是无意义的,因为没有指定对象。

  • static 修饰的成员又是特殊的,它们的存储空间不属于任何一个对象(它们是全局变 量),因此就算定义了某个对象,这些全局变量也不会被定义。
    既然 static int i; 一句是声明,那么我们就必须在别的地方给它一个定义,否则就会报错 undefined symbol(未定义的变量)。

所以说,我们必须在别的地方写上 int A::i; 这样 i 才有了定义。

我们来看一份完整的代码:

#include <iostream>

using namespace std;

class A
{
public:
  A(){ i = 0; }
  void Print() { cout << i << endl; }
  void Set(int ii){ i = ii; }
private:
  static int i;
};

int A::i;

int main()
{
  A a, b;
  a.Set(10);
  b.Print();
  return 0;
}

这份代码才可以正常运行,并且输出为 10。这充分的证明了变量 i 是一个全局变量,而不是某一个对象私有的。

有趣的是,全局变量不属于任意一个对象,因此它不可以在构造函数中初始化;但是它可以在构造函数中被赋值

换句话说:A() : i(0){} 是不可以的,而 A(){ i = 0; } 是可以的。

事实上,静态成员只能在它被定义的地方初始化,也就是 int A::i = 10; 这样就可以把 i 的值初始化为 10(尽管它是一个 private 的变量,看似不可以从外面访问)。

此外,虽然静态成员不属于任意一个对象,但是对象的 this 指针仍然指向 i。就像一个公用电话机,所有人都可以通过打电话访问到 i,但是所有人都不能说「i 是我的」。

以下三种方式都可以在有权限的情况下访问到静态变量 i: a.i, A::i, this -> i

静态成员函数

类似于静态成员变量,静态成员函数不属于任意一个对象,所以它不可以调用非静态成员变量

class A
{
public:
  A() : j(0) { i = 0; }
  void Print() { cout << i << endl; }
  static void Say(){ cout << i << ' ' << j << endl; }
private:
  static int i;
  int j;
};

这时无法通过编译,因为 Say 函数中调用 j,实际是调用 this -> j,但是 Say 函数不属于任意一个对象,它没有 this 指针;而 j 是一个非静态成员变量,j 的引用必须指定对象,因而引发了错误。

调用静态成员函数,也有三种方法:a.Say(), A::Say(), this -> Say();

Q:为什么 a.Say() 不会输出 a 的 j 呢?我已经指明了 a 去调用 Say 函数啊?
A:还是之前那个公用电话的例子:一个公用电话,我可以用这个电话打给 Say 函数,这样我可以找到 Say;但是 Say 打这个电话却找不到我。
也就是说,Say 不能反过来调用调用它的对象里的东西,因为所有对象(甚至不需要指定对象)都可以调用它。非静态函数则不同,它们每次被调用都必定是来自它们所归属的对象的调用操作,因此它们可以确定这次调用来自谁,因此也就可以访问归属于调用者的成员变量。

标签:变量,静态,int,Say,static,全局变量
From: https://www.cnblogs.com/zaza-zt/p/17857525.html

相关文章

  • Hadoop 环境变量配置
    1.下载https://hadoop.apache.org/releases.html2.环境变量配置在/etc/profile.d中配置1.新建hadoop.shvi/etc/profile.d/hadoop.shexportHADOOP_HOME=/usr/hadoop/hadoop-3.3.6exportPATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/bin2.授予文件执行权限chmodu+x/e......
  • 无涯教程-Linux - 特殊变量
    在本章中,我们将讨论Unix中的特殊变量,在上一章中,我们了解了变量名称中使用某些非字母数字字符时应注意的事项,这些变量保留用于特定功能。如,$字符代表当前Shell的进程ID号或PID-$echo$$上面的命令写入当前Shell的PID-29949下表显示了一些可以在Shell脚本中使用的特殊变量-......
  • 无涯教程-Linux - 变量声明
    在本章中,我们将学习如何在Unix中使用Shell变量,变量不过是指向实际数据的指针,Shell使您可以创建,分配和删除变量。变量名变量名称只能包含字母(a到z或A到Z),数字(0到9)或下划线字符(_),按照约定,Unixshell变量将以大写字母命名。以下示例是有效的变量名-_ALITOKEN_AVAR_1VAR_......
  • python 读取文件名中带有循环变量
    标题:Python中使用循环变量读取文件名的方法**摘要:**在Python编程中,我们经常需要读取并处理多个文件。而文件名中的循环变量可以帮助我们更加灵活地处理这种情况。本文将介绍如何使用Python中的循环变量来读取文件名,并给出相关的代码示例和详细说明。1.引言在实际的数据处理中,我......
  • 无涯教程-Linux - 环境变量
    在本章中,我们将详细讨论Unix环境。Unix的一个重要概念是environment,它是由环境变量定义的。变量是我们为其分配值的字符串。分配的值可以是数字,文本,文件名,设备或任何其他类型的数据。如,首先我们设置一个变量TEST,然后使用echo命令访问它的值-$TEST="UnixProgramming"$ec......
  • vue 根据js的变量来设置css 里面的属性的属性值
    1.通过动态绑定style,声明css变量"--fontColor",把变量”fontColor”赋给“--fontColor”2.在css中使用var函数读取“--fontColor”变量点击查看代码<template><divclass="wen_style":style="{'--fontColor':fontColor}">当前字体的颜色......
  • 8-Linux系统安装conda,镜像设置,环境变量
      1,我们现在学习Linux系统,首先得有个服务器的账号,这个要看自己的学校里面有没有专门做生信的课题组问问,或者去淘宝买,或者去那个组学大讲堂公众号里面有服务器租借的(没广告),租个好点的服务器,价格估计5000-10000半年不等的价格,能处理一些转录组等大数据,也可以做WGCNA等其他的大......
  • python变量和简单类型
    数据类型整型python的整型数值有4种表示形式:十进制形式二进制形式:以0b或0B开头的整数就是二进制形式的整数八进制形式:以0o或0O开头的整数就是八进制形式的整数十六进制形式:以0x或0X开头的整数就是十六进制形式的整数,其中10-15分别以a-f(a-f不区分大小写)来表......
  • 11.25-task3变量与函数
    变量与函数变量变量是一个名字,用来储存一段数据!变量的值可变化,新值会覆盖之前的值变量相当于一个标签,如a=10,在内存中找一个位置,把地址赋给了a。。命名规则数字不能开头!最好不要用保留字符作为变量名!!!!多变量赋值在进行多变量赋值时,需要将变量之间用,隔开等号右边......
  • C语言编程技巧 全局变量在多个c文件中公用的方法
    在使用C语言编写程序时,经常会遇到这样的情况:我们希望在头文件中定义一个全局变量,并将其包含在两个不同的C文件中,以便这个全局变量可以在这两个文件中共享。举个例子,假设项目文件夹"project"下有三个文件:main.c、common.c和common.h。在这种情况下,我们希望声明一个字符型变量"key",......