static和const都是C++中比较麻烦的东西,只要是太容易混淆他们的作用,之前对static做过点总结,但是不全面,还有很多小的错误,现在整理一下:
变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。
------------------------------------------------------------------------------------
『*』给出如下定义
“静态变量”这一术语有两个容易混淆的定义:
- 语言无关的通用定义:与程序有着相同生命周期的变量;
- C族语言特有的定义:以static存储类声明的变量。
而在以Pascal为代表的许多程序语言中,所有局部变量都由系统自动分配存储空间,而所有全局变量的存储空间则以静态分配的方式获取(对应“静态变量”),因此由于实际上“局部变量”和“全局变量”这两个术语已足以涵盖所有的情况,在这些程序语言中通常不使用“静态变量”这一术语,而直接以“全局变量”代之。一般来说,在这些程序语言中,静态变量就是全局变量,而即使在有明确区分全局和静态变量的程序语言中,在编译后的代码里二者也以相同的方式获取存储空间。而今术语“静态变量”的概念则主要基于C族语言的“static”的定义。
------------------------------------------------------------------------------------
按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。
按作用域分,全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。
通常,static的使用在三个方面:
(1)静态局部变量 (C语言)
(2)外部静态变量/函数 (C语言)
(3)静态成员变量/函数 (C++语言_类)
一.静态局部变量(一般定义在函数内部):
该情况下又可以分为三种功能:
a:隐藏
代码0:下面是a.C的内容:
char a = 'A'; // global variable void msg() { printf("Hello\n"); }
下面是main.C的内容:
int main(void) { extern char a; // extern variable must be declared before use printf("%c ", a); (void)msg(); return 0; }
程序的运行结果是:
A Hello
你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。
如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。
b:保持变量的持久
代码1:
#include <stdio.h> void func() { static int x = 0; // 在对func的三次调用中,x只进行一次初始化 printf("%d\n", x); // 输出x的值 x = x + 1; } int main(int argc, char * const argv[]) { func(); // 输出0 func(); // 输出1 func(); // 输出2 return 0; }
代码2:
//C中static的封装性。 //换句话说,封装在函数内的static变量只在本函数内部可见。 #include <iostream> using namespace std; int fun(void) { //static int count = 10; //事实上此赋值语句从来没有执行过 /*此时输出结果为:1 10;2 9;3 8;4 7......*/ int count = 10; //事实上此赋值语句从来没有执行过 /*此时输出结果为:1 10;2 10;3 10;4 10......*/ return count--; } int count = 1; //全局变量 void main() { cout<<"Output the data"<<endl;; for(; count <= 10; ++count) cout<<count<<' '<<fun()<<endl; }
此例中,static int count = 10将count分配在静态存储区,可见区域范围是fun()函数内部,而且其值在整个程序运行时均存在,并没有因为fun函数作用域的消失而消失,而是整个函数均保留。
全局变量也是存储在静态存储区,static和全局变量的区别在哪里?他们的可见区域不同,如上面的例子,若不用static那么改用全局变量也是可以,但是用全局变量,其他函数也可见,也可用,这会破坏函数的封装性。
c:具有默认值0
在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘\0’。不妨做个小实验验证一下。
代码3:
#include <stdio.h> int a; int main(void) { int i; static char str[10]; printf("integer: %d; string: (begin)%s(end)", a, str); return 0; }
程序的运行结果如下integer: 0; string: (begin)(end)
最后对static的三条作用做一句话总结
首先static的最主要功能是隐藏;
其次因为static变量存放在静态存储区,具有和全局变量相同的功能,所以它具备持久性和默认值0;
二.外部静态变量和函数
在C中 static有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。但为了限制全局变量/函数的作用域, 函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函 数)。注意此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区,生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
//C中static的隐藏性。 //file1.cpp static int varA; int varB; extern void funA() { …… } static void funB() { …… } //file2.cpp extern int varB; // 使用file1.cpp中定义的全局变量 extern int varA; // 错误! varA是static类型, 无法在其他文件中使用 extern vod funA(); // 使用file1.cpp中定义的函数 extern void funB(); // 错误! 无法使用file1.cpp文件中static函数
-----------------------------------------------------------------------------------------------
三. 静态成员变量和函数(C++)
对此,我们可以从两个方面考虑
a:静态成员变量
在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象*享的成员,而不是某个对象的成员。
使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
静态数据成员的使用方法和注意事项如下:
1、静态数据成员在定义或说明时前面加关键字static。
2、静态成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式如下:
<数据类型><类名>::<静态数据成员名>=<值>
这表明:
(1) 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。
(2) 初始化时不加该成员的访问权限控制符private,public等。
(3) 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。
3、静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化。
4、引用静态数据成员时,采用如下格式:
<类名>::<静态成员名>
如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。
我们通过下面的例子来解释C++中静态数据成员变量的问题:
#include <iostream> using namespace std; class Myclass { public: Myclass(int a, int b, int c); void GetSum(); private: int A, B, C; static int Sum; //define a private static member }; int Myclass::Sum = 0; //set value for the static member in the outclass Myclass::Myclass(int a, int b, int c) { A = a; B = b; C = c; Sum += A+B+C; } void Myclass::GetSum() { cout<<"Sum="<<Sum<<endl; } void main() { Myclass M(3, 7, 10),N(14, 9, 11); M.GetSum(); //输出结果为54 N.GetSum(); //输出结果为54 }
从输出结果可以看到Sum的值对M对象和对N对象都是相等的。这是因为在初始化M对象时,将M对象的三个int型数据成员的值求和后赋给了Sum,
于是Sum保存了该值。在初始化N对象时,对将N对象的三个int型数据成员的值求和后又加到Sum已有的值上,于是Sum将保存另后的值。所以,不论是通过对象M还是通过对象N来引用的值都是一样的,即为54。
b:静态成员函数
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员。
如果静态成员函数中要引用非静态成员时,可通过对象来引用。下面通过例子来说明这一点。
//调用静态成员函数
#include <iostream>
using namespace std;
class M
{
public:
M(int a) { A=a; B+=a;}
static void f1(M m);
private:
int A;
static int B;
};
void M::f1(M m)
{
cout<<"A="<<m.A<<endl;
cout<<"B="<<B<<endl;
}
int M::B=0;
void main()
{
M P(5);
M Q(10);
M::f1(P); //调用时不用对象名
M::f1(Q);
}
输出结果为:
5
15
10
15
很容易理解:当执行M P(5)时,调用构造函数,将5赋值给A,并将5赋值给静态变量B,然后执行M Q(10),由于B为静态成员变量,
所以B的第一次执行的结果保持不变,最后输出结果。
参考链接:http://www.cnblogs.com/Kane_zzt/archive/2009/05/18/1459697.html
http://www.yesky.com/20010828/194000.shtml