一 、函数内部的静态变量
在函数体内定义一个局部变量时,编译器在每次函数调用时,使堆栈的指针向下移动,为这些局部变量分配内存。
定义一个全局变量将不仅仅只受这个函数控制。
在C/C++函数内部定义的 static 对象,将存储在程序的静态数据区(static data area)中。
char * c = "abc" 这样的赋值会得到一个警告:不建议从字符常量到 char * 的转换,最好在前面加上 const
1 #include <iostream>
2 using namespace std;
3
4 char oneChar(const char * charArray = 0){
5 static const char * s;
6 if(charArray){
7 s = charArray;
8 return *s;
9 }else{
10 cout <<"un initialized\n";
11 }
12 if( * s == '\0'){
13 return 0;
14 }
15 return *s++;
16 }
17
18 char * a = "abcdefghijklmnopqrstuvwxyz";
19
20 int main(){
21 oneChar(a);
22 char c;
23 while((c = oneChar()) != 0)
24 cout << c <<endl;
25 return 0;
26 }
从上例可以看到 static const char * s 在每次函数调用时都保留了值。
可以看出来,在上面的函数中容易产生多线程的问题;在设计包含静态变量的函数时,应该记住多线程的问题。
二、静态对象的析构函数
静态对象的析构函数,在程序从main() 中退出时,或者标准C库的 exit()被调用时才被调用。(注意:使用abor() 来退出程序,静态对象的析构函数并不会调用)
如果一个包含局部静态对象的函数从未被调用,那么这个对象的构造函数也不会被调用。
同普通对象的销毁一样,静态对象的销毁也是按照与初始化相反的顺序进行的。
2 #include <fstream>
3 using namespace std;
4
5 ofstream out("statdest.out");
6
7 class Obj{
8 char c;
9 public:
10 Obj(char cc):c(cc){
11 out<<"Obj::Obj() for "<<c<<endl;
12 }
13 ~Obj(){
14 out<<"Obj::~Obj() for "<<c<<endl;
15 }
16 };
17
18 Obj a('a');
19
20 void f(){
21 static Obj b('b');
22 }
23
24 void g(){
25 static Obj c('c');
26 }
27
28 int main(){
29 out <<"inside main()"<<endl;
30 f();
31 out <<"leaving main()"<<endl;
32
33 return 0;
34 }
注意跟踪文件ofstream 的对象out 也是一个静态对象。
在C++ 中,全局静态对象的构造函数是在main()之前调用的,这样就有了一个在进入main() 之前执行一段代码的方法。
三、变量可见性
所有的全局对象都是隐含为静态存储的。
int a = 100;
则 a 被存储在程序的静态数据区,在进入main() 之前,a 已经初始化了。另外,a 对所有的编译单元都是全局可见的。用可见性术语来说,static(只在翻译单元内可见),extern 则明确声明对所有翻译单元都可见。
所以上面的定义和 extern int a = 0 是一样的
但如果这样定义:
static int a = 0 只是改变了a 的可见性,只在本翻译单元内,有时叫文件静态(file static),但并没有改变存储类型—— 仍然驻留在静态数据区,而不管是static 还是 extern
在局部变量中,就总是内部链接了,extern 的修饰没有意义;使用 static 时只会改变变量的存储类型。
四、定义静态数据成员的存储
因为类的静态数据成员有着单一的存储空间,所有必须在一个单独的地方定义,编译器不会分配存储空间,如果一个静态数据成员被声明但没有定义,则连接器会报告错误。
定义必须出现在类的外部(不允许内联),而且只能定义一次。
(1)这些变量唯一合法的初始化地方就是在定义时
(2)这些变量只能够被定义一次
以上两点来保证变量只被创建者所控制。
静态成员函数:
为类的全体对象服务。 可以用普通的方法调用静态成员函数,用“ .” 或者“->” 把它和一个对象相联系。一个更典型的方法是自我调用。
静态成员函数不能访问一般的数据成员,而只能访问静态的数据成员。原因在于:对象的地址(this)是被隐式传递到调用函数的;但static 函数没有this,因此无法访问一般成员。静态函数在速度上比全局函数有少许增长,不仅没有传递this所需要的额外开销,而且还有使得函数在类内的好处。
Static initialization dependency:
对于作用域为多个翻译单元的静态对象来说,不能保证初始化顺序,这可能会导致一些问题。