0%

C++ 中的 static

之前对 static 的理解仅限于:在类中声明这种类型的变量,可以通过这个变量知道这个类被创建了多少个对象。但是前些日子刷 leetcode 的时候,发现类中自定义的 cmp 函数如果不是 static 类型,就无法被类内的 sort 函数识别。所以今天来一探究竟。

面向过程

其实 C 语言中也有 static 这个关键字,在全局区分配内存,或者说可以理解为全局变量,来回顾一下:

  1. 在全局区分配内存,自动初始化为 0,注:全局区就是静态区
  2. 在声明的整个文件是可见的
  3. 不会因子函数的退出而被释放空间,即子函数被执行完成初始化后,之后的调用不会再初始化,但作用域是局部的
  4. 修饰普通函数,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。

全局变量:

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

static int test;

void sub_func() {
test += 1;
}

int main() {
test += 1;
std::cout << test << std::endl; // 1
sub_func();
std::cout << test << std::endl; // 2
return 0;
}

局部变量:

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

void sub_func() {
static int test;
test += 1;
std::cout << test << std::endl;
}

int main() {
// 无法访问 test
sub_func(); // 1
sub_func(); // 2
return 0;
}

面向对象

static 成员变量

  1. 这个类的所有对象都可以访问静态成员变量,一个对象修改,其他对象也会改变。说高级一些,不随对象创建而分配内存,不随对象销毁而释放内存
  2. 存储在全局区
  3. 必须类外初始化
  4. 可以通过类名访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

class A{
public:
static int num;
A(){
A::num += 1;
}
void increase(int a) {
// 类名访问
A::num += a;
}
};
// 类声明的外部初始化
int A::num = 0;

int main() {
A a;
A b;
std::cout << A::num << std::endl; // 2
b.increase(18);
std::cout << A::num << std::endl; // 20
return 0;
}

成员函数

  1. 静态成员函数仅可以访问静态成员、函数;不能访问非静态成员、函数
  2. 非静态成员函数可以任意访问静态成员、函数
  3. 不需要 this 指针的额外操作
  4. 可以通过类名访问

来看一个 static 的成员函数,顺便解答本文开始的疑问。

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
32
33
34
35
36
37
#include <iostream>
#include <algorithm>
#include <vector>

class A{
public:
static int num;
int a = 10;
static auto get_num() {
// 错误,不能访问非 static 变量
// 毕竟全局区不能访问堆栈区
// return a + A::num;
return A::num;
}
// 如果不加 static
// 错误:对非静态成员函数‘bool A::cmp(int&, int&)’的使用无效
// 因为 sort 这个函数是全局的,找不到类内的 cmp
static bool cmp(int& a, int& b) {
return a > b;
}
void sort(std::vector<int>& arr) {
std::sort(arr.begin(), arr.end(), cmp);
}
};
// 类声明的外部初始化
int A::num = 0;

int main() {
A a;
A b;
std::vector<int> arr{3, 2, 4, 6, 2, 5, 7};
a.sort(arr);
for (auto i : arr) {
std::cout << i << " ";
}
return 0;
}

:: 扩展

上面写了很多 ::,江湖人称范围解析运算符,顺手总结一下:

  1. ::variable,全局作用域符,作用域是全局空间,如 ::isspace
  2. class::variable,某个类的作用域范围
  3. namespace::variable,某个命名空间的作用域范围。重点是,static 声明的链接性为内部静态变量的东西,可以使用未命名的 namespace 代替。
感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章