C++ static关键字

C++ static关键字

static关键字可用于声明全局范围、命名空间范围和类范围变量和函数。 静态变量还可在本地范围声明。

先介绍几个概念:

  1. 静态持续时间,在程序启动时分配对象或变量,并在程序结束时释放对象或变量。

  2. 外部链接,变量的名称在用于声明变量的文件的外部是可见的。

  3. 内部链接,名称在用于声明变量的文件的外部是不可见的。

默认情况下,在全局命名空间中定义的对象或变量具有静态持续时间和外部链接。

static 关键字常用于以下情况:

  1. 在全局和/或命名空间范围 (在单个文件范围内声明变量或函数时,) static 关键字指定变量或函数为内部链接,即外部文件无法引用该变量或函数。在声明变量时,变量具有静态持续时间,并且除非您指定另一个值,否则编译器会将变量初始化为 0
  2. 在函数中声明变量时, static 关键字指定变量只初始化一次,并在之后调用该函数时保留其状态
  3. 在类内声明中声明数据成员时, static 关键字指定该类的所有实例共享该变量的副本。 必须在文件范围内定义静态数据成员。
  4. 在类声明中声明成员函数时, 关键字指定该函数由类的所有实例 static 共享。 静态成员函数无法访问实例成员,因为该函数没有隐式 this 指针。 若要访问实例成员,请使用作为实例指针或引用的参数来声明函数。
  5. 不能将联合成员声明为静态的。 但是,必须显式声明全局声明的匿名联合 static

以上前两条与面向对象(类)无关,也就是说在 C 中 static 同样有这些特性,而后三条则是针对类的,在 C++ 中才有这些特性。

C中的static

本小节介绍 C 中 static 关键字的作用,即不包含 C++ 中引入面向对象的特性(类)之后 static 的作用,该部分将在下一小节介绍。

内部链接

函数,变量均可被 static 修饰来实现内部链接。当同时编译多个文件时,所有未加 static 前缀的全局变量和函数默认都是外部链接的。举例来说明。同时编译两个源文件,一个是 hello_a.c,另一个是 main.c

// hello_a.c
#include <stdio.h>
char a = 'A'; // a.c 文件内的全局变量
void printHello()
{
     printf("Hello\n");
}
// main.c
#include <stdio.h>
void msg();		// 外部方法不声明会报警告
int main()
{
     extern char a; // 外部变量必须先用 extern 关键字声明
     printf("%c \n", a);
     printHello();
     return 0;
}

编译运行:

gcc main.c  hello_a.c
./a.out

输出:

A
Hello

为什么在 hello_a.c 中定义的全局变量 a 和函数 printHello 能在 main.c 中使用?前面说过,所有未加 static 前缀的全局变量和函数都都是外部链接的,其它的源文件也能访问。此例中,a 是全局变量,printHello 是函数,并且都没有加 static 前缀,因此对于另外的源文件 main.c 是可见的。

如果加了 static,就会对其它源文件隐藏。例如在 a 和 printHello 的定义前加上 static,main.c 就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static 可以修饰函数和变量,将其对其他源文件隐藏起来,从而避免命名冲突。对于函数来讲,static 的作用仅限于该隐藏功能

保持变量内容的持久

static的第二个作用是保持变量内容的持久,即static变量中的记忆功能和全局生存期。

存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。之后再次运行到含有 static 关键字的初始化语句时不会再执行该语句。共有两种变量存储在静态存储区:全局变量和 static 变量,只不过和全局变量比起来,static 可以控制变量的可见范围。

PS:如果作为 static 局部变量在函数内定义,它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。

在下面的例子中,nStatic的值仅在初次调用 showstat 函数时初始化,之后在每次调用showstat函数时,nStatic的值都是保持的(而非重新初始化)。

// static1.cpp
#include <iostream>

using namespace std;
void showstat( int curr ) {
   static int nStatic;    // nStatic的值仅在初次调用时初始化,之后在每次调用showstat函数时,
                          // nStatic的值都是保持的
   nStatic += curr;
   cout << "nStatic is " << nStatic << endl;
}

int main() {
   for ( int i = 0; i < 5; i++ )
      showstat( i );
}

运行结果:

nStatic is 0
nStatic is 1
nStatic is 3
nStatic is 6
nStatic is 10

综合以上两点,有:

  • 把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围
  • 把局部变量(函数内)改变为静态变量后是改变了它的存储方式,从而改变了它的生存期

也就是说 static 这个关键字在不同的地方所起的作用是不同的。

默认初始化为0

static的另一个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是 ‘\0’。不妨做个小实验验证一下。

#include <stdio.h>

int a;

int main()
{
     int i;
     static char str[10];
     printf("integer: %d; string: (begin)%s(end)\n", a, str);
     return 0;
}

输出:

integer: 0; string: (begin)(end)

C++中的static

本小节主要是介绍在 C++ 中引入了面向对象的特性(类)之后,static 关键字的一些用途。当然之前的 C 中的 static 在 C++ 中也是成立的。

在类中声明 static 变量或者函数时,初始化时使用作用域运算符 :: 来标明它所属类。静态数据成员是类的成员,而不是对象的成员,这样就出现以下作用:

  1. 使用 static 关键字来修饰成员函数或变量,是指成为静态成员函数或变量。

  2. 类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。

  3. static 关键字不能修饰虚函数

  4. 由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊 ,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember 函数指针”。

  5. 由于静态成员函数没有this指针,所以就差不多等同于 nonmember 函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X W indow系统结合,同时也成功的应用于线程函数身上。 (这条没遇见过)

  6. static 并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。

  7. 静态数据成员是静态存储的,所以必须对它进行初始化。 (程序员手动初始化,否则编译时一般不会报错,但是在Link时会报错误)

  8. 静态成员初始化与一般数据成员初始化不同:

    • 声明在类内,初始化赋值在类外;

    • 初始时赋值前面不加 static,以免与一般静态变量或对象相混淆;

    • 初始化时赋值不加该成员的访问权限控制符private,public等;

    • 初始化时赋值使用作用域运算符 :: 来标明它所属类;

    所以我们得出静态数据成员初始化的格式:<数据类型><类名>::<静态数据成员名>=<值>如:int myClass::num = 10,在后面会有例子;

  9. 为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志;

  10. 不论在类内声明的还是在成员函数中声明的 static 变量,都是整个类的所有实例对象共享一份副本。

以下是例程:

类内声明的static变量

// static2.cpp
#include <iostream>

using namespace std;
class CMyClass {
public:
   static int m_i;
};

int CMyClass::m_i = 0;
CMyClass myObject1;
CMyClass myObject2;

int main() {
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject1.m_i = 1;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject2.m_i = 2;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   CMyClass::m_i = 3;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;
}

结果输出:

0
0
1
1
2
2
3
3

CMyClass 内的 static 变量 m_i 是整个类共享的,在该类的每个对象 myObject1myObject2 中都一样。

成员函数内声明的static变量

// static3.cpp
#include <iostream>
using namespace std;
struct C {
   void Test(int value) {
      static int var = 0;
      if (var == value)
         cout << "var == value" << endl;
      else
         cout << "var != value" << endl;

      var = value;
   }
};

int main() {
   C c1;
   C c2;
   c1.Test(100);
   c2.Test(100);
}

结果输出:

var != value
var == value

Ref:

cnblogs.com/songdanzju/p/7422380.html

https://docs.microsoft.com/en-us/cpp/cpp/storage-classes-cpp?view=msvc-170

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值