c++ static详解
此文资料来自CU的一篇精华帖,将static的知识总结的比较全面到位,故整理来与大家分享。
通常理解static只是指静态存储的概念,事实上在c++里面static包含了两方面的含义。
1)在固定地址上的分配,这意味着对象是在一个特殊的静态区域上创建的,而不是每次函数调用的时候在堆栈上动态创建的,这是static的静态存储的概念。
2) 另一方面,static能够控制对象对于连接器的可见性。一个static对象,对于特定的编译单元来说总是本地范围的,这个范围包括本地文件或者本地的某一个类,超过这个范围的文件或者类是不可以看到static对象的,这同时也描述了连接的概念,它决定连接器能够看到哪些名字。
一,关于静态存储
对于一个完整的程序,一般程序的由malloc,realloc产生的动态数据存放在堆区,程序的局部数据即各个函数内部的数据存放在栈区,局部数据对象一般会随着函数的退出而释放空间,对于static数据即使是函数内部的对象则存放在全局数据区,全局数据区的数据并不会因为函数的退出就将空间释放。
C的文件作用域对象,C++的全局(命名空间作用域)变量具有静态存储期,关于静态存储期,c和c++标准有这样的描述:
ISO C11(N1570)
6.2.4
3 An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
ISO C++11
3.7.1 Static storage duration [basic.stc.static]
1 All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program (3.6.2, 3.6.3).
二,函数内部的静态变量
通常,在函数体内定义一个变量的时候,编译器使得每次函数调用时候堆栈的指针向下移动一个合适的位置,为这些内部变量分配内存。如果这个变量是一个初始化表达式,那么每当程序运行到这儿的时候程序都需要对表达式进行初始化。这种情况下变量的值在两次调用之间则不能进行保存。
有的时候我们却需要在两次调用之间对变量的值进行保存,通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制。因此我们有必要将变量声明为static对象,这样对象将保存在静态存储区域,而不是保存在堆栈中。对象的初始化仅仅在函数第一个被调用的时候进行初始化,每次的值保持到下一次调用,直到新的值覆盖它。下面的例子解释了这一点。
1 //****************************************
2
3 // 1.cpp
4
5 //****************************************
6
7 #include <iostream.h>;
8
9 void add_n(void);
10
11 void main(){
12
13 int n=0;
14
15 add_n();
16
17 add_n();
18
19 add_n();
20
21 }
22
23 void add_n(void){
24
25 static int n=50;
26
27 cout<<”n=”<<n<<endl;
28
29 n++;
30
31 }
程序运行结果为:
1. n=50
2. n=51
3. n=52;
从上面的运行结果可以看出static n确实是在每次调用中都保持了上一次的值。如果预定义的静态变量没有提供一个初始值的话,编译器会确保在初始化的时候将用零值为其初始化。static变量必须被初始化,但是零值初始化仅仅只对系统预定义类型有效,比如int,char,bool等等。事实上我们用到的不仅仅是这些预定义类型,大多数情况下可能用到结构,联合或者类等各种用户自定义的类型,对于这些类型用户必须使用构造函数进行初始化。如果我们在定义一个静态对象的时候没有指定构造函数的参数,那就必须使用缺省的构造函数,如果缺省的构造函数也没有的话则会出错。看下面的例子。
1 //**************************************************
2
3 #include <isotream.h>;
4
5 class x{
6
7 int i;
8
9 public:
10
11 x(int i=0):i(i){
12
13 cout<<”i=”<<i<<endl;
14
15 }//缺省构造函数
16
17 ~x(){cout<<”x::~x()”<<endl;
18
19 };
20
21 void fn(){
22
23 static x x1(47);
24
25 static x x2;
26
27 }
28
29 main(){
30
31 fn();
32
33 }
程序运行结果如下:
1. i=47
2. i=0
3. x::~x()
4. x::~x()
从上面的例子可以看出静态的x对象既可以使用带参数的构造函数进行初始化,象x1,也可以使用缺省的构造函数,象x2。程序控制第一次转到对象的定义点时候,而且只有第一次的时候才需要执行构造函数。如果对象既没有带参数的构造函数又没有缺省构造函数则程序将无法通过编译。
三,类中的静态成员
static静态存储的概念可以进一步引用到类中来。c++中的每一个类的对象都是该类的成员的拷贝,一般情况下它们彼此之间没有什么联系,但有时候我们需要让它们之间共享一些数据,我们可以通过全局变量来实现,但是这样的结果是导致程序的不安全性,因为任何函数都可以对这个变量进行访问和修改,而且容易与项目中的其余的名字产生冲突,因此我们需要一种两全其美的方法,既可以当成全局数据变量那样存储,又可以隐藏在类的内部,与类本身联系起来,这样只有类的对象才可以操纵这个变量,从而增加了变量的安全性。
这种变量我们称之为类的静态成员,静态成员包括静态数据成员和静态成员函数。类的所有的静态数据成员有着单一的存储空间而不管类的对象由多少,这些对象共享这块存储区域。因此每一个类的对象的静态成员发生改变都会对其余的对象产生影响。先看下面的例子。
1 //**************************************
2
3 // student.cpp
4
5 //**************************************
6
7 #include <iostream.h>;
8
9 #include <string.h>;
10
11 class student{
12
13 public:
14
15 student(char* pname=”no name”){
16
17 cout<<”create one student”<endl;
18
19 strcpy(name,pname);
20
21 number++;
22
23 cout<<number<<endl;
24
25 }
26
27 ~student() {
28
29 cout<<”destruct one student”<<endl;
30
31 number--;
32
33 cout<<number<<endl;
34
35 }
36
37 static number(){
38
39 return number; }
40
41 protected:
42
43 char nme[40]
44
45 static int number;
46
47 };
48
49 void fn(){
50
51 student s1;
52
53 student s2;
54
55 cout<<student::number<<endl;
56
57 }
58
59 main(){
60
61 fn();
62
63 cout<<student::number<<endl;
64
65 }
程序输出结果如下:
create one student
1
create one student
2
2
destruct one student
1
destruct one student
0
0
上面的程序代码中我们使用了静态成员变量和静态成员函数,下面我们先阐述静态数据成员。
四,静态成员变量
在代码中我们可以看出,number既不是对象s1也
补充:软件开发 , C++ ,