Static关键字

static关键字在C++中有两个意思,这取决于上下文,其中之一是在类或结构体外使用static关键字,另一种是在类或结构体内使用static。类外部的static静态变量,意味着你声明为static的符号,链接只是在内部,这意味这它只能对你定义它的翻译单元可见。类内部的static静态变量,将与类的所有实例共享内存,这意味着该静态变量在你类中创建的所有实例中只有一个实例。对于static方法而言,这在告诉编译器这个方法只会在当前翻译单元中被用到,其他翻译单元不会调用,这将会影响链接过程。

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

类与结构体之外的静态

Example 1

1
2
3
4
5
6
7
8
9
//main.cpp
#include <iostream>

int s_val = 10;

int main() {
std::cout << s_val << std::endl;
return 0;
}
1
2
//Static.cpp
static int s_val = 5;

文件结构如上,可以成功编译,main输出10

Example 2

1
2
//Static.cpp
int s_val = 5;

去掉static就会报错,链接错误

一种解决方案是使用extern,告诉编译器在别的翻译单元寻找s_Val的定义

1
2
3
4
5
6
7
8
9
//main.cpp
#include <iostream>

extern int s_val;

int main() {
std::cout << s_val << std::endl;
return 0;
}

编译运行,main的输出值是5。

Example 3

如果尝试将Static.cpp的变量修改为

1
2
//Static.cpp
static int s_val = 5;
1
2
3
4
5
6
7
8
9
//main.cpp
#include <iostream>

extern int s_val;

int main() {
std::cout << s_val << std::endl;
return 0;
}

cmake编译报错undefined reference to `s_val’,因为链接器在全局作用域下找不到Static.cpp里的s_val。

类或结构体内的静态

如果类成员变量被static修饰,意味着该类的所有实例均共享同一个该静态成员变量。静态方法无法访问类的实例,因为你不知道应该访问类的哪个实例。静态类变量和静态类方法不需要通过实例进行访问。

Example 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

struct Entity {
static int x, y;

void Print() {
std::cout << x << "," << y << std::endl;
}
};

int main() {
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 4;
e1.y = 5;
e.Print();
e1.Print();
return 0;
}

上述代码编译失败,因为类或结构体的静态变量需要在类或结构体之外定义,类的静态成员变量需要在类外分配内存空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

struct Entity {
static int x, y;

void Print() {
std::cout << x << "," << y << std::endl;
}
};
int Entity::x;
int Entity::y;
int main() {
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 4;
e1.y = 5;
e.Print();
e1.Print();
return 0;
}

成功编译通过。

实际上修改静态成员变量可以使用如下方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

struct Entity {
static int x, y;

void Print() {
std::cout << x << "," << y << std::endl;
}
};

int Entity::x;
int Entity::y;

int main() {
Entity e;
Entity::x = 2;
Entity::y = 3;
e.Print();
return 0;
}

静态方法同理。

Example 2

静态方法不能访问非静态成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

struct Entity {
int x, y;

static void Print() {
std::cout << x << "," << y << std::endl;
}
};

int main() {
Entity e;
e.x = 2;
e.y = 3;
Entity::Print();
return 0;
}

IDE静态检查无法通过。因为每个非静态方法总是会获取当前类的一个实例作为参数。

C++中的局部静态

声明一个变量我们需要考虑两个事情,变量的生存期和变量的作用域。静态局部变量相当于在函数内部声明了一个变量,它的生命周期基本上相当于整个程序的生命周期,但是它的作用范围被限制在这个函数内部。你可以在任何作用域中声明局部静态变量,可以是函数内,也可以是if语句块内,或者其他地方。

Example 1

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

void Func() {
static int i = 0;
i++;
std::cout << i << std::endl;
}

int main() {
Func();
Func();
return 0;
}

不多解释,输出1和2。如果去掉static,不管调用Func几次,输出都一样。