870920 Menu

SwingCoder之C++备忘录·22

static详解

类内的变量、对象和函数声明为static后,即具有全局性质和外部链接属性。函数外的static数据和函数,具有全局性质,但仅能本编译单元内部链接。全局数据同样具有静态性质,无论是否声明为static,只是存储的内存区不同。程序启动时首先为static数据分配内存并在整个运行期内使之常驻内存,即:所有静态对象在使用前均确保被初始化。尽量避免使用全局变量,而以类内的static来替代之。

注意:全局属性、可见性与链接属性(可用性)是不同的概念,这一点要区分开来。

1、类内的static成员

使用static静态数据成员和静态成员函数的目的有6:

1、取代全局变量和全局函数,将全局变量和全局函数纳入类的范围内,限定其名字空间(类名),非public性质的static成员,其作用域仅限于所属的类。public性质的static成员,无需实例化类的对象即可使用,语法为:类名::static成员名(前提是执行此语句时,该成员可见)。

2、解决了同一个类的多个对象之间的数据共享。

3、使类本身天然具有某种属性和能力,而无需借助实例化的对象来实现这些属性和能力。

4、清晰直观的表明编程意图,即静态成员为类所有,不与任何对象或普通数据成员发生关系。

5、调用方无需创建本类对象即可正常调用本类的public静态函数和public静态数据成员。

6、节省空间(该类的所有对象共享一份副本),提高效率(无需每次都要创建和初始化等操作)。可将一些频繁使用并固定不变的对象声明为全局性的静态const对象,此时,这些全局数据仅具有内部链接属性。
/** 静态const字串。全局性的。*/
static const wchar_t* miscPage = L”杂项”;
static const wchar_t* aboutPage = L”关于”;

类的普通成员,每个对象各有一份,互不干涉,各行其是,而静态成员在整个程序中只有一份。类本身和类的所有对象使用静态成员时用的都是同一份数据。换个角度,静态成员不属于任何一个对象,它属于类本身的固有属性和能力。即使当前尚未实例化任何对象,也能正常使用静态成员(前提是该static成员是public性质的)。当然,也可以用对象调用本类的静态成员。如需使用private静态成员,则可间接通过类的public静态函数来完成。

类的静态数据成员可声明为任意类型的,可以是任意内置类型、常量、引用、数组、类类型等等。静态数据成员不能在类的构造函数中初始化,而是类内声明,类外初始化(整个程序中只能初始化一次),静态数据成员的初始化必须在该类实例化对象之前完成。因此,通常在类声明之后,类的成员函数定义之前初始化静态数据成员,这条语句通常放在类的源文件的开头处,而非类的头文件中:

类型 类名::静态数据成员的名称 = 初始化式; // 类外定义和初始化静态数据成员

一个非常特殊和违反直觉的例外是const类型的整型静态数据成员。这种形式的静态数据成员可在类中声明时就做初始化,但初始化式必须是字面常量值、常量表达式或枚举值,同时,依然要在类外定义一次,只不过,类外定义时无需再给其赋初始值。这种情况下,该数据成员等同于常量表达式,在指定类的数组的大小时比较有用。

// 类中声明const整型静态数据成员,声明的同时用字面常量值初始化之
static const int arraySize = 64;
const int 类名::arraySize; // 类外依然要定义该静态数据成员,只是不必再赋初始值

注意两点:类外定义静态成员无需使用static前缀;类的static数据成员不能声明为mutable。

类的静态成员函数与普通成员函数的异同:
 二者均可以随意使用类的静态成员。
 静态成员函数没有隐含的this指针。如果类中的某个成员函数为被其它函数所回调的函数(该函数以函数指针的形式作为其它函数的参数),则必须将其声明为static函数(即该函数不需要也不能有this指针)。
 静态成员函数无法调用类的普通数据成员,也不建议调用。
 静态成员函数没有虚函数一说,也没有常函数一说,无法被继承,也不能实现运行时多态。
 如果类的某个成员函数不访问本类的普通数据成员,也不调用本类的普通成员函数,则最好将该函数声明为static静态函数

2、static全局对象和static全局函数

类外的对象或函数声明为static,意味着该对象或函数是static全局性的,常驻内存,整个运行期间一直存在,从声明位置起,仅在本编译单元后续部分中的所有代码均可使用,其他编译单位则不可用(此规则延续C语言的static语法,即内部链接属性)。不建议使用全局性的static对象和函数。

3、函数中的static对象(变量)

即局部静态数据,该数据位于某个函数体内,其可见性仅为该函数。第一次调用该函数时,创建该数据并初始化,以后再调用,则不会再次创建之(而且自动保留上次调用时的值),因为该数据是常驻内存的,而非离开作用域即自动销毁,因此,可利用这一特性获知某个函数被调用的次数,或者实现其他特殊目的。示例:
int getCallNums ()
{
static int n = 1; // 函数体中声明static局部变量,仅在此函数中可见
// … do something..
return ++n;
}