关键字 Static
[TOC]
1. static变量
1.1 静态局部变量
static关键字在局部变量中的应用是其最常见的用法之一。
静态局部变量仅在函数第一次调用时初始化,而在函数调用结束后仍然保留其值。
这对于需要在多次调用之间保留状态的函数非常有用。
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
void demoStaticLocalVariable() {
static int count = 0;
count++;
std::cout << "Function called " << count << " times." << std::endl;
}
int main() {
demoStaticLocalVariable();
demoStaticLocalVariable();
demoStaticLocalVariable();
return 0;
}
在上面的例子中,count是一个静态局部变量。每次调用demoStaticLocalVariable函数时,count都会递增,但其值在函数调用之间保持不变。这提供了一种在函数调用之间保持状态的简便方法。
另外,当你在一个类的成员函数中声明一个变量为 static,这个变量的存储持续性变为静态的,它就成为了与类相关联而非与任何特定对象相关联的变量。这意味着无论你创建多少个对象,静态局部变量只有一份拷贝,并且其值在对象之间共享。静态局部变量的生命周期从它们被初始化开始,直到程序结束。它们的初始化只会在第一次进入声明它们的函数域时发生一次,后续的函数调用不会重新初始化它们。
1.2 静态全局变量
与静态局部变量类似,静态全局变量也只初始化一次,但其作用域超出了单个函数。
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
static int globalCount = 0;
void demoStaticGlobalVariable() {
globalCount++;
std::cout << "Function called " << globalCount << " times." << std::endl;
}
int main() {
demoStaticGlobalVariable();
demoStaticGlobalVariable();
demoStaticGlobalVariable();
return 0;
}
在这个例子中,globalCount是一个静态全局变量。无论在哪个函数中调用,globalCount都会在函数调用之间保持状态。
1.3 全局变量和静态全局变量的区别
在C++(以及C)中,全局变量和静态全局变量主要的区别在于它们的链接性(linkage),也就是它们的可见性或作用域范围。1
除了链接性的区别,静态全局变量和非静态全局变量在其他方面是相似的。它们都有静态存储期(static storage duration),意味着它们在程序的整个运行期间都存在,并且它们会在程序启动之前被初始化(如果没有显式初始化,它们会被自动初始化为0)。
(1) 全局变量:
- 全局变量在整个程序中都是可见的,除非它被隐藏了(例如,因为有同名的局部变量)。
- 它们在程序的任何地方都可以被访问(前提是它们已经被声明了,通常使用extern关键字在其他文件中声明)。
- 全局变量有外部链接性(external linkage),意味着在其他文件中可以通过extern声明访问同一个变量。
(2) 静态全局变量:
- 静态全局变量只在定义它的文件内可见,这被称为文件作用域。它们对其他文件是隐藏的。
- 它们在定义它们的文件之外不能被直接访问(即使使用extern关键字也不行)。
- 静态全局变量有内部链接性(internal linkage),意味着即使其他文件中有相同名字的变量,它们也是完全不同的变量。
下面是一个示例来说明这两者间的区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// File1.cpp
#include <iostream>
int globalVar = 0; // 全局变量
static int staticGlobalVar = 0; // 静态全局变量
void incrementAndPrint() {
globalVar++;
staticGlobalVar++;
std::cout << "globalVar in File1: " << globalVar << "\n";
std::cout << "staticGlobalVar in File1: " << staticGlobalVar << "\n";
}
// File2.cpp
#include <iostream>
extern int globalVar; // 使用extern关键字声明外部链接的全局变量
// extern int staticGlobalVar; // 错误!staticGlobalVar在这个文件中不可见
void anotherFunction() {
globalVar++;
// staticGlobalVar++; // 错误:这个变量在File2中不可见
std::cout << "globalVar in File2: " << globalVar << "\n";
}
int main() {
incrementAndPrint(); // 输出 globalVar 和 staticGlobalVar 的值
anotherFunction(); // 只会影响 globalVar 的值
incrementAndPrint(); // 再次输出 globalVar 和 staticGlobalVar 的值
return 0;
}
在这个例子中,File1.cpp 定义了一个全局变量 globalVar 和一个静态全局变量 staticGlobalVar。
globalVar 可以在 File2.cpp 中被访问和修改,因为它有外部链接性。
而 staticGlobalVar 只能在 File1.cpp 中被访问和修改,因为它有内部链接性,它在 File2.cpp 中是不可见的。
1.4 static成员变量
在类中,static关键字还可以用于声明静态成员变量。静态成员变量是类的所有实例共享的,而不是每个实例都有自己的一份。
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
class MyClass {
public:
static int staticMemberVariable;
};
int MyClass::staticMemberVariable = 0;
int main() {
MyClass obj1;
MyClass obj2;
obj1.staticMemberVariable = 42;
std::cout << obj2.staticMemberVariable << std::endl; // 输出 42
return 0;
}
在这个例子中,staticMemberVariable是MyClass的静态成员变量。即使有多个MyClass的实例,它们都共享相同的staticMemberVariable。
2. 函数的static修饰
2.1 静态函数
static关键字还可用于修饰函数,使其成为静态函数。静态函数只能在声明它的文件中可见,作用范围也被限制在当前文件内,无法被其他文件引用。
这有利于模块化设计,减少不必要的外部依赖。这有助于限制函数的可见性,提高代码的安全性。可以避免在其他文件中引用不应被外部访问的函数。
例如,现有一文件file1.c:2
1
2
3
4
5
6
7
8
9
10
11
12
13
// file1.c
#include <stdio.h>
// 静态函数
static void secretFunction() {
6 printf("This is a secret function!\n");
}
// 可以调用静态函数
int main(int argc, char *argv[]) {
secretFunction();
return 0;
}
在file2.c文件中,调用file1.c中的secretFunction函数,则会失败
1
2
3
4
5
6
7
8
9
// file2.c
// 错误:无法在其他文件中访问静态函数
extern void secretFunction();
int main(int argc, char *argv[]) {
// 错误:无法在其他文件中访问静态函数
secretFunction();
return 0;
}
2.2 静态类成员函数
在类中,static关键字可以用于声明静态成员函数。与普通成员函数不同,静态成员函数不依赖于类的实例,可以直接通过类名调用。
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
class MyClass {
public:
static void staticMemberFunction() {
std::cout << "This is a static member function." << std::endl;
}
};
int main() {
MyClass::staticMemberFunction();
return 0;
}
在这个例子中,staticMemberFunction是一个静态类成员函数。通过类名MyClass直接调用,而不需要创建类的实例。