Static关键字作用

基本概念

  1. 使用目的:

    在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

  1. 存放位置

    DATA 段(全局初始化区)存放初始化的全局变量和静态变量;BSS 段(全局未初始化区)存放未初始化的全局变量和静态变量。其中BBS段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化

  2. 使用static的优势:

    • 可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。
    • 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的。即静态全局变量不能被其它文件所用;其它文件中可以定义相同名字的变量,不会发生冲突。
  3. 动态数据vs静态数据:

    一般程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。

静态数据成员

静态数据成员的生存期大于 class 的对象,静态数据成员是每个 class 有一份,普通数据成员是每个 instance 有一份,因此静态数据成员也叫做类变量,而普通数据成员也叫做实例变量。

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
#include <iostream>

using namespace std;

class Test
{
public:
int n = 0;
// 类内的声明只是声明了静态成员变量的存在,类外的定义才真正分配内存并初始化它。
static int i;
};

// 静态成员变量属于类本身,而不是类的某个实例,因此它们的内存分配和初始化必须在类外进行。
// 在类外初始化时,必须再次指定变量的类型,以确保编译器知道该变量的类型和所属类。
int Test::i = 0;

int main()
{
Test test1;
Test test2;
test1.i += 1;
test1.n += 1;
cout << test2.i << endl; // 输出为1
cout << test2.n << endl; // 输出为0
return 0;
}

静态成员函数

静态成员函数不能访问非静态(包括成员函数和数据成员),但是非静态可以访问静态。因静态成员函数不属于类实例,它获取不了实例的信息,而非静态成员能获取全局静态数据,所以它可以调用静态数据。

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
#include <iostream>

using namespace std;

class Test
{
public:
int n = 0;
static int i;
static void printi()
{
cout << i << endl;
}
void printn()
{
cout << n << endl;
}
};

int Test::i = 0;

int main()
{
Test test1;
Test test2;
test1.i += 1;
test1.n += 1;
cout << test2.i << endl;
cout << test2.n << endl;
test1.printi();
test2.i += 1;
Test::printi(); // 输出为2,因test2也给i加了1
// Test::printn(); // 报错,因printn得由实例来call
test1.printn(); // 实例call就okay
return 0;
}

局部静态变量

实现局部能一直使用一个静态变量,而其他域不能访问的静态变量。

比如,记录一个函数被调用了多少次:

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

void count()
{
static int num = 0; // 这里就将num放到了静态区,所以不会被局部域销毁;初始赋值只会被执行一次
++num;
printf("called %d\n", num);
}

int main()
{
count(); // called 1
count(); // called 2
count(); // called 3
return 0;
}

静态全局变量

限制全局变量的获取权限,只让当前文件访问,不让其他文件访问

1
2
3
4
5
6
7
// file1
int b;
static int a;

// file2
extern int b; // 成功
extern int a; // 报错显示找不到a,因为file1中的a是静态的,其他文件访问不到

Extern关键字作用

  1. 允许多个文件访问同一个全局变量/函数
  2. 在当前文件表明变量/函数来自于其他文件
  3. 外来函数默认在外面找,不用extern关键字
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
// file1
#include <iostream>

using namespace std;

int i = 5, j = 6;

int add(int num1, int num2)
{
return num1 + num2;
}

// file2
#include <iostream>
#include "file1.cpp"
using namespace std;

extern int i, j; // 声明取的extern的全局变量
extern int i = 0; // 会报错,说重复定义
int add(int, int); // 只用声明一下add函数,不需要extern关键字

int main()
{
cout << i << endl; // 输出为5
printf("add result: %d\n", add(i, j));
return 0;
}

指针和引用

指针

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。