static关键字如果不被引入C++语言,那就违反了C++设计中对低级语言设计支持规则中的”没有无故与C语言不兼容规则”,原因很简单,C语言中存在static并发发挥着它良好的作用,所以C++同样引入static应该是理所当然的,而实际C++的做法是不仅引入static,而且对它在面向对象程序设计中进行扩充(导入静态数据成员和静态函数成员的概念),这就使static的概念得到了扩展,对于其中较新的静态成员变量(对象)和静态函数成员则应该重点更理解,下面分别从static的作用,在C++中的分类,以及一些比较典型的应用三个方面进行总结一下
一:静态(static)的作用
首先,一个很明显的作用就是解决了全局部名字空间的问题,大家都知道全局变量是一处定义多处修改的,这当然是它的优势但,也确实解决了不少问题,但这对面向对象程序思想是相抵触的,它破坏的良好的封装性,污染了程序的名称空间,而对于这些缺点的,正是static能够解决的问题,我们可以在函数,结构,类中定义静态对象,当将变量定义为static时,编译器就会将变量的存储在程序的静态存储区(数据段)域而非普通的函数分配的栈空间上。
其次,static会将其修修饰的变量对象的可见性限制在本编译单元内(也就是后所以的具有文件作用域),使它成为一个内部连接。这与普通全局变量加入”extern”可以在多个文件中使用是相对应的。
二:静态(static)的分类
目前在C++语言有五种静态对象的类别,分别为静态局部变量(对象),静态全局变量(对象),静态函数(静态全局函数),静态成员变量(对象),静态成员函数。
静态局部变量,静态全局变量,以及静态函数的存在是对C语言兼容的结果,静态员成员变量是在C++引入类之后相对应存在的概念,静态成员函数则是为保持类的封装的前提下对静态成员变量进行更好方法而引入的一相概念,所以说静态成员函数就目前来讲是所以静态对象(包括C++所有的静态变量和静态成员函数)中最晚一个引入C++语言的。
1. 静态局部变量(对象)
静态局部变量(对象)通也是指在函数中定义的静态变量(对象),当函数第一次被调用时,程序会为它在程序的数据段分配存储空间,并对它进行初始,当函数调用完成退出时, 这个变量(对象)会保存此次函数调用后的状态, 但不会被其它函数使用, 当再次调用该函数时, 并不为它分配空间和初始化,而是直接命使用上次调用的结果。看看下面简单的程序:
////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <cstdlib>
using namespace std;
void foo(bool i)
{
static int a=0;
int c=0;
if(i){
static int b=0;
++b;
cout <<"b="<< b <<endl;
}
++a;
++c;
cout << "a="<< a <<endl;
cout << "c="<< c <<endl;
}
int main()
{
foo(false); //变量,a,c初始化化并加1,所以a=1,c=1,此是b并没有初始化
//和分配空间,函数调用完全c的空间被回收,a由于是静态变量,
//所以并没有回收.
cout << "-----------------" <<endl;
foo(true); //变量a在上次调用的状态上加1,所以a=2,c重新初始分配空间并加1,
//所以c=1,变量b初始化并分配空间后加1,所以b=1.
cout << "-----------------" <<endl;
foo(false);//变量a在上次调用的状态上加1,所以a=3,c重新初始分配空间并加1,
//所以c=1,变量b保自己的状态,所以b=1.
cout << "-----------------" <<endl;
foo(true);//变量a在上次调用的状态上加1,所以a=4,c重新初始分配空间并加1,
//所以c=1,变量b在上次调用的状态上加1,所以b=2.
system("PAUSE");
return 0;
}
//////////////////////////////////////////////////////////////////
从程序的运行结可以看出局部静态变量只在第一次遇到是进空间的分配和构造,函数调用完成并不立即回收,而等程序运行的结束.正因为它只存在一共享空间地址(对象多线程来讲),所以在多线程序程序中为保正确的读取而需要对其进行锁定。
2. 静态全局变量(对象)
静态全局变量(对象)是指全局变量的基础上加上static修饰符,它同时具有文件作用域和静态生存期两种特性。具体来讲就是指只在定义它的源文件中可见而在其它源文件中不可见的变量(文件作用域)。它与全程变量的区别是: 全程变量可以再说明为外部变量(extern), 也就是指跨文件作用域,被其它源文件使用, 而静态全程变量却不能再被说明为外部的, 即只能被所在的源文件使用。
3. 静态函数(静态全局函数)
静态函数一般来说相对较少用到普通函数若未加static修饰,具有跨文件作用域,若加static修饰,与静态全局变量相类似也具有文件作用域,一般函数本身的一个目标就是实现共用(不管是函数定义文件还是被作为include对象的文件),若在函数前加上static主使得函数只能在本文件中使用,其它文件不能使用,我们当然不想这样做啦J,所以,在一般的代码中,很少函数静态函数(静态全局函数)不存在。
4. 静态成员变量(对象)
当在程序中这义静态变量(对象)时,那么对于这个类的所以对象都将只分配一个共享的存储单,简单的说,静态成员变量属于类范围,而不是具体的某个对象,相当于类范围内的全局变量(对象),当然也可以说是属于自身类型的所有对象,它的值是可以更新的。只要对静态成员变的值更新一次,所有对象存取者将是更新后的相同的值(多线程应该自行控制),这样可以提高时间效率。
静态成员变量(对象)以为类为名称空间,为了更好的实现信息隐藏,可以将它设置为private,然后通过函数进行存取(后面应用中将会详细讲到),程序对类静态数据成员的访问会被转换为类内部的惟一extern实体的直接操作(至于如何被转换,不同的编译器将会有不同的算法,要详细了解请参阅《Inside C++ Object Model》),正因为这样,所以在类的内部可以直接定义自身类型的static成员,而不会造成类定义的无限递归,而非静态的自身类型则不行,只能是自身类型的指针或引用,其实这跟类定义结束是到大括号后的分号为止是有关的,在此之前将无法确定类的大小,而自身类型的指针或引用的大小则是确定(一个指针的空间)。如下面代码:
class A{
//……
private:
//……
static A a1;//ok,因为a1为静态成员对象。
A a2;//error此时不能确定A类型的大小。
A* a3;//ok 此时能确定A*类型的大小,一个指抽A类型的指针的大小
A& a4;//ok 此时能确定A&类型的大小,一个指抽A类型的指针的大小
};
与全局变量一样,静态成员变量也只能提供一次这义,类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以它的实初始化将不能被放在头文件中,而应该放入相就是类成员函数的实现文件中,也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。对于const类型的静态有序类型可以直接初始化,但其定义还要在类定义之外:
//头文件
class print{
private:
static const int count = 10;//直接初始化
string info[count];
};
const int print::count;//可以在头文件,也可以在实现文件,因为不会为常分配空间
5. 静态成员函数
静态成员函数的引入实际是为了更好存取静态成员变量(对象),静态成员变量(对象)并不局限于某个具体的对象,而普通成员函数则不为具体的某个对象服务的,那么当用普通成员函数进行静态成员变量(对象)存取就会造成将静态成员变量约束为具体的某个对象(原因是因为普通成员函数约束于具体的某个对象),使用静态成员函数将会很好的解决这些问题。所以静态成员函数具有静态成员变量(对象)相类似的特点,不管何种形式(一般包括具体对象,具体对象的指针或引用,::运算符,静态成员函数指针)的静态员函数的调用将会被转换成非成员函数(普通函数)的调用。
静态成员函数最大的特点就是没有this指针,从而导至下面一些特点:
1.不能直接存取类中的非态成员,只能直接存取静态成员变量(对象)。
从语言层次来说普通成员函数能够存取非静态成员靠是this指针,静态成员函数没有this指针,所以无法实现。
2.不能为const,volatile或virtual。
静态成员函数本向就不能存取非静态成员,所以const对它来说不有意义。virtual说明在一个类继承体系中不同类可能不多个不同的实现实体,而静态成员函数则只有一个,所以static对virtual并不适合(个人理解,仅供参考,也有可能是错误码的噢J)
3.不需要约束于类对象才能被调用。
静态成员函数本身就是为了解决此问题而被提出的J.
4.可以作为回调函数(CallBack)。
静态成员函数摆脱的this指针的约束而成为普通的函数。