C++ static关键字

来源:互联网 发布:java爬虫代码视频教程 编辑:程序博客网 时间:2024/06/08 08:41

一.面向过程编程中的static关键字

1.静态全局变量

静态全局变量:
[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9. static int s_test;  
  10. int nons_test;  
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.       
  15.     //静态全局变量默认初始化为0  
  16.     cout<<"static :"<<s_test<<endl;  
  17.     //非静态全局变量默认初始化也是0,但如果是局部非静态变量,在VS中直接报错,不能通过编译  
  18.     cout<<"non-static: "<<nons_test<<endl;  
  19.   
  20.     system("pause");  
  21.     return 0;  
  22. }  

结果:
static :0
non-static: 0
请按任意键继续. . .

我们在运行程序时,内存分为代码区,全局数据区,堆区,栈区。正常的临时变量auto等都在栈区,生命周期是函数结束,而new出来的对象一般都在堆区,声明周期由我们控制,new时开始,delete时结束。而全局数据区则保存全局变量以及静态变量,他们的生命周期是整个程序的运行周期。

使用静态全局变量和使用普通全局变量的差别:
1)如果全局变量在头文件中,或者使用和定义都在同一个文件中,静态全局变量和普通全局变量是相同的。
[cpp] view plain copy
  1. //StaticTest.h头文件中分别定义静态全局变量和普通全局变量  
  2. static int static_num = 20;  
  3. int nonstatic_num = 30;  

[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //全局变量在头文件或者本文件中两者没有什么区别  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <string>  
  7. #include "StaticTest.h"  
  8. #include "StaticTest1.h"  
  9. using namespace std;  
  10.   
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.       
  15.     //如果静态变量在头文件中或者本文件中,可以访问静态全局变量  
  16.     cout<<"static :"<<static_num<<endl;  
  17.     //如果静态变量在头文件中或者本文件中,可以访问非静态全局变量  
  18.     cout<<"non-static: "<<nonstatic_num<<endl;  
  19.   
  20.     system("pause");  
  21.     return 0;  
  22. }  

结果:
static :20
non-static: 30
请按任意键继续. . .


2)如果全局变量在.cpp文件中,普通全局变量是全局可见的,即其他文件也可见,但是要使用时,就要在其他文件中添加extern关键字。而且如果不添加的话,在这个文件再声明同名的变量,是会报错的。但是使用静态全局变量就可以解决这个问题,静态全局变量在其他文件中是不可见的,我们不需要关注其他文件中的全局变量,也不会出现不能使用同名全局变量的问题啦!

[cpp] view plain copy
  1. //.h文件  
  2. #ifndef __STATICTEST1_H_  
  3. #define __STATICTEST1_H_  
  4. #pragma once  
  5. class StaticTest1  
  6. {  
  7. public:  
  8.     StaticTest1(void);  
  9.     virtual ~StaticTest1(void);  
  10. };  
  11. #endif  
  12.   
  13. //.cpp文件  
  14. #include "stdafx.h"  
  15. #include "StaticTest1.h"  
  16.   
  17. //在.cpp文件中定义全局变量  
  18. static int static_test = 10;  
  19. int nonstatic_test = 20;  
  20.   
  21. StaticTest1::StaticTest1(void)  
  22. {  
  23. }  
  24.   
  25.   
  26. StaticTest1::~StaticTest1(void)  
  27. {  
  28. }  
main函数文件:
[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include "StaticTest1.h"  
  7. using namespace std;  
  8.   
  9. extern int nonstatic_test;  
  10. extern int static_test;  
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.       
  15.     //如果静态变量在其他.cpp文件中,不可以访问静态全局变量,即使在本文件使用extern声明也不行  
  16.     //cout<<"static :"<<static_test<<endl;  
  17.     //如果静态变量在其他.cpp文件中,可以访问非静态全局变量,但是要在本文件使用extern声明  
  18.     cout<<"non-static: "<<nonstatic_test<<endl;  
  19.   
  20.     system("pause");  
  21.     return 0;  
  22. }  

不添加extern时,静态全局变量为未定义,而即使添加extern声明也会报错:
C++Test2.obj : error LNK2001: 无法解析的外部符号 "int static_test" (?static_test@@3HA)
1>: fatal error LNK1120: 1 个无法解析的外部命令


关于静态全局变量,有下面三点要注意:
1)静态全局变量如果未初始化,会被默认初始化为0,而非静态全局变量,注意是全局变量,也是初值为0。(非静态非全局变量如果未初始化在VS中会直接报错的)
2)静态全局变量在全局数据区分配内存,所以变量的生存期是整个程序的运行周期。(局部静态变量的生存期也是整个程序运行周期)
3)静态全局变量在变量声明的文件是可见的,但是在其他文件中是不可见的。

关于全局变量:
如果我们将全局变量直接放在头文件中,变量会随着头文件的引入,引入到各个文件中。有时候这是我们不想看到的,所以另一种方法是将全局变量放在.cpp文件中,这样,全局变量就不会随着.h文件到处引入,不是全局可见。但是,这样这个变量仍然是全局变量,要想在另外的文件中使用这个变量,就要在这个文件中使用extern关键字声明一下。如果不声明,就会出现变量未声明的情况。如果直接在这个变量再定义一个同名的变量,就会出现冲定义的情况,这也是我们不想看到的,如果全局变量仅仅在本文件中有用,那么不如直接使用静态全局变量。


2.静态局部变量

局部变量,即存储在栈空间的变量,我们在调用函数时,变量初始化,而函数调用结束时,局部变量的生存期就结束了,进而被销毁。而有时候我们需要对两次调用函数之间的变量进行保存,最简单的想法就是使用一个全局变量,但是这个变量就已经不属于函数本身,而是全局可见,不符合局部性原理,给维护带来了不便。而静态局部变量刚好可以解决这个问题。静态局部变量存储在全局数据区,不会因函数结束而销毁,并且在其他函数看来,静态局部变量是不可见的,刚好满足了我们的需求!

下面看一个例子:

[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <string>  
  7. #include <vector>  
  8. using namespace std;  
  9.   
  10.   
  11. void TestFunc()  
  12. {  
  13.     //定义一个静态局部变量,但是这个变量仅仅在第一次的时候初始化,再次调用时不会再初始化  
  14.     static int static_num = 10;  
  15.     //但是普通局部变量,每次都会初始化  
  16.     int nonstatic_num = 10;  
  17.     static_num++;  
  18.     nonstatic_num++;  
  19.     cout<<"non_static: "<<nonstatic_num<<"  static :"<<static_num<<endl;  
  20. }  
  21.   
  22. int _tmain(int argc, _TCHAR* argv[])  
  23. {  
  24.     //调用10次该函数  
  25.     for(int i = 0; i < 10; i++)  
  26.         TestFunc();  
  27.   
  28.     system("pause");  
  29.     return 0;  
  30. }  

结果:

non_static: 11  static :11
non_static: 11  static :12
non_static: 11  static :13
non_static: 11  static :14
non_static: 11  static :15
non_static: 11  static :16
non_static: 11  static :17
non_static: 11  static :18
non_static: 11  static :19
non_static: 11  static :20
请按任意键继续. . .


从上面的结果我们看出,函数被调用了10次,非静态变量每次都被初始化,因而结果没变。而静态变量却只被初始化了一次,因而每次结果都比上一次大1。


关于局部静态变量要注意的几点:

1)局部静态变量也在全局数据区分配内存,不会因为函数调用结束而销毁。

2)局部静态变量在首次调用到该变量的时候进行初始化,之后再次调用时不会再进行初始化。并且局部静态变量一般就在声明处初始化,如果未显示初始化,则默认初始化为0

3)局部静态变量的生命周期为声明时到程序结束,但是它的作用域却是局部的,仅仅在该函数内,不会破坏局部性原理。


3.静态函数

函数默认是全局可见的,而如果我们希望某个函数只在本文件中可见,那么可以将它声明为静态函数。这一点与静态全局变量差不多。
一个例子:
[cpp] view plain copy
  1. //StaticTest.h  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5.   
  6. void NormalFuncTest();  
  7.   
  8. static void StaticFuncTest();  

[cpp] view plain copy
  1. //StaticTest.cpp  
  2. #include "stdafx.h"  
  3. #include "StaticTest.h"  
  4.   
  5.   
  6. void NormalFuncTest()  
  7. {  
  8.     cout<<"Normal Func!"<<endl;   
  9. }  
  10.   
  11. static void StaticFuncTest()  
  12. {  
  13.     cout<<"Static Func!"<<endl;  
  14. }  

[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //main函数  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include "StaticTest.h"  
  7. using namespace std;  
  8.   
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     NormalFuncTest();  
  13.     StaticFuncTest();  
  14.   
  15.     system("pause");  
  16.     return 0;  
  17. }  
这个例子中有两个函数,都定义在另一个文件中,一个为普通函数,另一个为static函数。编译一下,会出现如下错误:

\vs2012\vs12\vc\include\xlocnum(155): error C2129: 静态函数“void StaticFuncTest(void)”已声明但未定义

就是说,编译器只看到了.h文件中的声明,但是看不到.cpp文件中的实现。这样,static的目的就达到啦!但是,如果将函数的实现放到了.h中,那么,不管是不是静态的,都是可以使用的。


二.面向对象编程中的Static关键字

1.静态数据成员

在类中,成员也可以声明为静态数据成员,而在面向对象编程中,static关键字又有了新的功能。
先来一个例子:
[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. using namespace std;  
  7.   
  8.   
  9. class StaticTest  
  10. {  
  11. private:  
  12.     //此处为声明,声明一个静态成员变量  
  13.     static int count;  
  14.     int id;  
  15. public:  
  16.     StaticTest(int i): id(i)  
  17.     {  
  18.         count++;  
  19.     }  
  20.   
  21.     void Show()  
  22.     {  
  23.         cout<<"ID: "<<id<<endl;  
  24.     }  
  25.   
  26.     static void ShowCount()  
  27.     {  
  28.         cout<<"Count: "<<count<<endl;  
  29.     }  
  30. };  
  31.   
  32. //在类外定义并初始化静态成员变量,由于定义时需要分配空间,所以不能在类声明中定义  
  33. int StaticTest::count = 0;  
  34.   
  35.   
  36. int _tmain(int argc, _TCHAR* argv[])  
  37. {  
  38.   
  39.     StaticTest test1(1);  
  40.     StaticTest test2(2);  
  41.     test1.Show();  
  42.     test2.Show();  
  43.     StaticTest::ShowCount();  
  44.   
  45.     system("pause");  
  46.     return 0;  
  47. }  

结果:
ID: 1
ID: 2
Count: 2
请按任意键继续. . .

关于静态成员变量有下面几点:
1)静态成员变量在一个类中只有一份,由该类所有的对象所共享,静态数据成员只分配一次内存。而非静态成员变量则是每个对象都有一份自己的拷贝。
2)静态成员变量遵循public,private,protected的访问规则
3)静态数据成员在全局数据区分配内存,属于本类所有对象共享,但是它不属于任何一个对象,所以即使没有产生对象时也可以使用静态成员变量。
4)静态成员变量的初始化比较特殊,不能直接像普通成员变量那样在类中给初值,更不能像非成员变量那样直接初始化,静态成员变量需要在类的定义中声明,然后再类外面,用<类型><类名>::<变量名> = <值>的格式来初始化。如上面的例子中:int StaticTest::count = 0;
5)访问静态成员变量的方式也有两种,一种是与普通成员变量的访问方式一样,<对象名>.<静态数据成员>。另一种是静态成员变量特有的方式<类名>::<静态数据成员>



2.静态成员函数

还是上面的那个例子,加了一部分功能:

[cpp] view plain copy
  1. // C++Test.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. using namespace std;  
  7.   
  8.   
  9. class StaticTest  
  10. {  
  11. private:  
  12.     //此处为声明,声明一个静态成员变量  
  13.     static int count;  
  14.     int id;  
  15. public:  
  16.     StaticTest(int i): id(i)  
  17.     {  
  18.         count++;  
  19.     }  
  20.   
  21.     void Show()  
  22.     {  
  23.         //但是非静态成员函数可以访问静态成员函数和变量  
  24.         cout<<"ID: "<<id<<"  Count: "<<count<<endl;  
  25.     }  
  26.   
  27.     static void ShowCount()  
  28.     {  
  29.         //静态成员函数不能访问非静态的成员,非静态成员函数也不行!  
  30.         //cout<<"ID: "<<id<<endl;     error C2597: 对非静态成员“StaticTest::id”的非法引用  
  31.         //Show();:                    error C2352: “StaticTest::Show”: 非静态成员函数的非法调用  
  32.         cout<<"Count: "<<count<<endl;  
  33.     }  
  34. };  
  35.   
  36. //在类外定义并初始化静态成员变量,由于定义时需要分配空间,所以不能在类声明中定义  
  37. int StaticTest::count = 0;  
  38.   
  39.   
  40. int _tmain(int argc, _TCHAR* argv[])  
  41. {  
  42.   
  43.     StaticTest test1(1);  
  44.     StaticTest test2(2);  
  45.     test1.Show();  
  46.     test2.Show();  
  47.     StaticTest::ShowCount();  
  48.   
  49.     system("pause");  
  50.     return 0;  
  51. }  

结果:

ID: 1  Count: 2
ID: 2  Count: 2
Count: 2
请按任意键继续. . .


关于静态成员函数注意的地方:

1)静态成员函数可以访问静态成员函数和静态成员变量。但是静态成员函数不能访问普通成员变量和普通成员函数。(因为静态成员函数没有this指针,属于共用)

2)非静态成员函数可以访问静态成员变量和静态成员函数。

3)定义在类外的静态成员函数不能加static,声明时加个static即可,定义时和普通成员函数相同。

4)静态成员函数与静态成员变量的调用规则一致,都不需要有对象就能调用。可以使用正常方法,也可以使用类名::调用。


三.扩展分析

术语static有着不寻常的历史.起初,在C中引入关键字static是为了表示退出一个块后仍然存在的局部变量。随后,static在C中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用static关键字来表示这第二种含义。最后,C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数(与Java中此关键字的含义相同)。

全局变量、静态全局变量、静态局部变量和局部变量的区别:

变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。

(1) 按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。

(2) 按作用域分,  全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。

     全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

    从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。

(1) static 函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

(2) static全局变量与普通的全局变量有什么区别:static全局变量只初始化一次,防止在其他文件单元中被引用;

(3) static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
(4) static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.
(5) 全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。


http://blog.csdn.net/puppet_master/article/details/47347695

http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777441.html


0 0
原创粉丝点击