由于内存的空间有限,我们常常关心已经使用掉了多少内存空间。如果我们修改上一章的链表程序(程序15.5),要能计算出整个程序一共产生了多少链表结点,我们该怎么做呢?
显然,我们需要一个计数器。每产生一个结点,计数器就加一;每消除一个结点,计数器就减一。由于结点的产生和消除只会与链表类或结点类的某些成员函数有关,所以这个计数器只能是一个全局变量了(全局变量的概念见11.1节),否则它将无法被各个成员函数访问和修改。
不过使用全局变量会带来严重的安全性问题。产生了多少个链表结点明明是和结点类有关的,却没有被封装在结点类里面。任何函数都能修改这个全局变量,不得不让我们担忧。
封装在类内部的数据是成员数据。想象一下,如果我们给链表结点类添加一个成员数据count,那么链表结点类的定义就是这样:
class Node//定义一个链表结点类
{
public:
……
//成员函数同程序15.5
private:
int idata;//存储数据保密
char cdata;//存储数据保密
Node *prior;//前驱结点的存储位置保密
Node *next;//后继结点的存储位置保密
int count;//新来的成员函数,用于记录产生了多少个结点
};
现在计数器是一个成员数据,可以被链表结点类的成员函数访问,也保证它不会被随便修改。但如果我们创建了三个结点对象a、b、c之后,我们发现a.count、b.count和c.count是三个互不相关的变量,也就是说它们的值可能是不一致的。更麻烦的是,我们不知道还会产生多少结点对象,如果新增一个结点对象,那么之前的每一个结点对象的count都要发生变化!
所以,我们需要一种方法,既能把count封装在类的内部,又能使各个对象的count相同。
我们将产生的结点个数记为count,它不是某一个结点所具有的属性,而应该是整个链表结点类所具有的属性,或者说它是各个结点对象的共有属性。
如果我们把idata和cdata比作每个结点的私有财产,那么count就是所有结点的共有财产。count能被任何一个结点使用,但事实上无论有多少个结点,count只有一个。这样就不会发生a.count、b.count和c.count各不相同的情况了。在C++中,用静态成员数据(Static Data Member)来描述这种共有属性。与一般的成员数据类似,静态成员数据也可以分为公有(Public)的和私有(Private)的。静态成员数据的声明方法为:
static 数据类型成员变量名;
下面我们来看看如何给链表结点类增加一个静态成员数据:
class Node//定义一个链表结点类
{
public:
……
//成员函数同程序15.5
private:
int idata;//存储数据保密
char cdata;//存储数据保密
Node *prior;//前驱结点的存储位置保密
Node *next;//后继结点的存储位置保密
static int count;//私有静态成员数据,用于记录产生了多少个结点
};
由于静态成员数据不是仅仅属于某一个具体对象的,所以它不能在构造函数中被初始化。(否则岂不是每创建一个对象,静态成员数据都要被初始化一次?)如果类的头文件会被直接或间接地重复包含,则静态成员数据也会被重复初始化。为了避免这个问题,我们可以将类的声明和定义分离,如果忘记了这个问题可参见11.2节。如果类的头文件绝对不会被重复包含,那么把静态成员数据的初始化放在类的头文件中也是可以勉强接受的。
静态成员数据的初始化语句为:
数据类型类名::静态成员数据=初始值;