0%

C++static关键字

C++static关键字

static关键字

  • 面向对象 (变量、函数)
    • 静态成员变量
    • 静态成员函数
  • 面向过程
    • 静态全局变量
    • 静态局部变量
    • 静态函数

静态成员变量

在类内成员变量的声明前加上关键字static,该数据成员就是类内的静态数据成员。

静态成员变量特点:

  1. 静态成员变量是该类的所有对象所共有的。对于普通成员变量,每个类对象都有自己的一份拷贝。而静态成员变量一共就一份,无论这个类的对象被定义了多少个,静态成员变量只分配一次内存,由该类的所有对象共享访问。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
  2. 因为静态数据成员在全局数据区分配内存,由本类的所有对象共享,所以,它不属于特定的类对象,不占用对象的内存,而是在所有对象之外开辟内存,在没有产生类对象时其作用域就可见。因此,在没有类的实例存在时,静态成员变量就已经存在,我们就可以操作它;
  3. 因为静态成员变量 不随 一个类的实例化 而重新开辟内存。所以 静态成员变量,必须在类外定义,如例子中的 int Myclass::Sum; 在这儿定义了之后,才会被分配内存,因此不定义的话就会报错。但是可以不初始化,静态成员变量存储在全局数据区,编译器就默认0.
  4. static 成员变量和普通 static 变量一样,编译时在静态数据区分配内存,到程序结束时才释放,不随对象的销毁而释放内存。
  5. 静态成员变量定义 初始化 可以不加 static 关键字 如例子。<数据类型><类名>::<静态数据成员名>=<值>
  6. 静态成员变量可以不通过 类对象 访问
  7. sizeof 运算符不会计算 静态成员变量
  8. 静态数据成员和普通数据成员一样遵从public,protected,private访问规则;

### 应用场景

如果想在同类的多个对象之间实现数据共享,又不要用全局变量,那么就可以使用静态成员变量。静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处:

  1. 不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,节省存储空间。
  2. 一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了。

相对全局变量的优点

  1. 静态成员变量没有进入程序的全局命名空间,因此不存在与程序中其它全局命名冲突的可能。
  2. 可以实现信息隐藏。静态成员变量可以是private成员,而全局变量不能。
1
2
3
4
class CMyclass{
int n;
static int s;
}; //则sizeof(CMyclass)等于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
25
26
27
28
29
30
31
32
33
class Myclass
{
public:
Myclass(int a,int b,int c){
this->a=a;
this->b=b;
this->c=c;
Sum+=a+b+c;
}
void GetSum(){
cout<<"Sum="<<Sum<<endl;
}
private:
int a,b,c;
static int Sum;//声明静态数据成员
};

int Myclass::Sum=10; //定义并初始化静态数据成员 不初始化的话 编译链接通不过

int main()
{
Myclass M(1,2,3);
M.GetSum();
Myclass N(4,5,6);
N.GetSum();
M.GetSum();
return 0;
}
/* 输出
Sum=16
Sum=31
Sum=31
*/

静态成员函数

与静态成员变量类似,我们也可以声明一个静态成员函数。

特点

  1. 出现在类体外的函数定义不能指定关键字static; (就是static关键字只能出现在类里)
  2. 静态成员之间可以相互访问,即静态成员函数(仅)可以访问静态成员变量、静态成员函数;
  3. 静态成员函数不能访问非静态成员函数和非静态成员变量;
  4. 非静态成员函数可以任意地访问静态成员函数和静态数据成员;
  5. 由于没有this指针的额外开销,静态成员函数与类的全局函数相比速度上会稍快; (也正是因为没有this指针,静态成员函数不能访是虚函数,也不能访问非静态成员)
  6. 调用静态成员函数,两种方式 通过对象 和 不通过对象 调用

拷贝构造函数的问题

例如下面的例子中,调用拷贝构造函数生成临时隐藏类对象时,这个临时对象在消亡时会调用析构函数 会 执行 totle_num–。简言之,就是在使用 拷贝构造时 对静态成员变量的操作要注意时 成对的 否则会出bug

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//Example 6
#include <iostream>
using namespace std;

class Student{
private:
char *name;
int age;
float score;
static int num; //学生人数
static float total; //总分
public:
Student(char *, int, float);
void say();
static float getAverage(); //静态成员函数,用来获得平均成绩
};

int Student::num = 0;
float Student::total = 0;

Student::Student(char *name, int age, float score)
{
this->name = name;
this->age = age;
this->score = score;
num++;
total += score;
}

void Student::say()
{
cout<<name<<"的年龄是 "<<age<<",成绩是 "<<score<<"(当前共"<<num<<"名学生)"<<endl;
}

float Student::getAverage()
{
return total / num;
}

int main()
{
(new Student("小明", 15, 90))->say();
(new Student("李磊", 16, 80))->say();
(new Student("张华", 16, 99))->say();
(new Student("王康", 14, 60))->say();
cout<<"平均成绩为 "<<Student::getAverage()<<endl;
return 0;
}

运行结果:
小明的年龄是 15,成绩是 90(当前共1名学生)
李磊的年龄是 16,成绩是 80(当前共2名学生)
张华的年龄是 16,成绩是 99(当前共3名学生)
王康的年龄是 14,成绩是 60(当前共4名学生)
平均成绩为 82.25

静态全局变量

特点

  1. 变量在全局数据区分配内存;
  2. 未经初始化的静态全局变量会被程序自动初始化为0(自动变量的自动初始化值是随机的);
  3. 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;  

对于一个完整的程序,在内存中的分布情况如下:

【代码区】【全局数据区】【堆区】【栈区】

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

优点

  1. 静态全局变量不能被其它文件所用;(即使在其它文件中使用extern 进行声明也不行)
  2. 其它文件中可以定义相同名字的变量,不会发生冲突;

静态局部变量

在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。

通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,这给程序的维护带来不便。静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。

特点

  1. 静态局部变量在全局数据区分配内存;
  2. 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化
  3. 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
  4. 静态局部变量始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

静态函数

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。

  1. 静态函数不能被其它文件所用;
  2. 其它文件中可以定义相同名字的函数,不会发生冲突;