10.3 C++中的静态成员

有时需要为某个类的所有对象分配一个单一的存储空间。在C语言中,可以用全局变量,但这样很不安全。全局数据可以被任何人修改,而且,在一个大项目中,它很容易与其他的名字相冲突。如果可以把一个数据当成全局变量那样去存储,但又被隐藏在类的内部,并且清楚地与这个类相联系,这种处理方法当然是最理想的了。

这一点可以用类的静态数据成员来实现。类的静态成员拥有一块单独的存储区,而不管创建了多少个该类的对象。所有的这些对象的静态数据成员都共享这一块静态存储空间,这就为这些对象提供了一种互相通信的方法。但静态数据属于类,它的名字只在类的范围内有效,并且可以是public(公有的)、private(私有的)或者protected(保护的)。

10.3.1 定义静态数据成员的存储

因为类的静态数据成员有着单一的存储空间而不管产生了多少个对象,所以存储空间必须在一个单独的地方定义。编译器不会分配存储空间。如果一个静态数据成员被声明但没有定义时,连接器会报告一个错误。

定义必须出现在类的外部(不允许内联)而且只能定义一次,因此它通常放在一个类的实现文件中。这种规定常常让人感到很麻烦,但它实际上是很合理的。例如,在一个类中定义一个静态数据成员如下:

10.3 C++中的静态成员 - 图1

之后,必须在定义文件中为静态数据成员定义存储区:

10.3 C++中的静态成员 - 图2

如果要定义了一个普通的全局变量,可以这样:

10.3 C++中的静态成员 - 图3

在这里,类名和作用域运算符用于指定了A:i。

有些人对A:i是私有的这点感到疑惑不解,可是在这里似乎在公开地直接对它处理。这不是破坏了类结构的保护性吗?有两个原因可以保证它绝对的安全。第一,这些变量的初始化惟一合法的地方是在定义时。事实上,如果静态数据成员是一个带构造函数的对象时,可以调用构造函数来代替“=”操作符;第二,一旦这些数据被定义了,最终的用户就不能再定义它—否则连接器会报告错误。而且这个类的创建者被迫产生这个定义,否则这些代码在测试时无法连接。这就保证了定义只出现一次并且它是由类的构造者来控制的。

静态成员的初始化表达式是在一个类的作用域内,请看下例:

10.3 C++中的静态成员 - 图4

这里,withStatic:限定符把withStatic的作用域扩展到全部定义中。

10.3.1.1 静态数组的初始化

第8章介绍了静态常量(static const)变量,它允许在一个类体中定义一个常量值。也可以创建静态对象数组,包括const数组与非const数组。这同前面的语法是一致的。

10.3 C++中的静态成员 - 图5

10.3 C++中的静态成员 - 图6

利用全部类型的静态常量,可以在类内提供这些定义,但是对于其他的对象(包括全部类型的数组,甚至它们为静态常量),必须为这些成员提供专门的外部定义。这些定义是内部连接的,所以可以把它放在头文件中,初始化静态数组的方法与其他聚合类型的初始化一样,包括自动计数。

也可以创建类的静态常量对象和这样的对象的数组。不过,不能使用“内联语法”初始化它们,这种语法对全部的内建类型的静态常量有效。

10.3 C++中的静态成员 - 图7

10.3 C++中的静态成员 - 图8

类对象的常量和非常量静态数组的初始化必须以相同的方式执行,它们遵守典型的静态定义语法。