C++开发初级


842 浏览 5 years, 3 months

5.1 静态数据成员

版权声明: 转载请注明出处 http://www.codingsoho.com/

静态数据成员

有时没必要让所有类对象都包含某个变量的副本,或者这么做无法完成特定的任务。数据成员可能只对类有意义,而不适用于拥有其副本的每个对象。

例如,您或许想给每个电子表格一个唯一的数字m,您需要一个从0开始的计数器,每个对象都可以从这个计数器得到自身的ID。

电子表格的计数器确实属于Spreadsheet类,但没必要使每个Spreadsheet对象都包含这个计数器的副本,因为您必须让所有的计数器都保持同步。

C++用静态(static)数据成员解决了这个问题。静态数据成员是属于类而不是对象的数据成员,您可将静态数据成员当作属于类的全局变量。

下面是spreadsheet类的定义,其中包含了新的数据成员静态计数器:

class Spreadsheet
{
 protected:
  static int sCounter = 0;
  //static int sCounter;    // Pre C++11
}; 

代码取自 SpreadsheetDataMembers\Spreadsheet.h

在C++11中,这就是您要做的全部工作。 如果您使用的C++版本早于C++11,那么稍微有一点麻烦。那样的话将无法在类定义中初始化静态成员,您不仅要在类定义中列出static类成员,还需要在源文件中为其分配内存,通常是定义类方法的那个源文件。

在此您还可以初始化静态成员,但注意与普通的变量以及数据成员不同,在默认情况下它们会被初始化为0。static指针会被初始化为nullptr。

下面的代码是C++11之前的编译器为sCounter分配空间并初始化的代码:

int Spreadsheet::sCounter = 0;

代码取自 SpreadsheetDataMembers\Spreadsheet.cpp

这行代码在函数或者方法外部,与声明全局变量非常类似,只是使用了作用域解析Spreadsheet::指出这是Spreadsheet类的一部分。

在类方法内访问静态数据成员

在类方法内部可以像使用普通数据成员那样使用静态数据成员。例如,您或许想要为Spreadsheet类创建一个mId成员,并在Spreadsheet构造函数中用sCounter成员初始化它。下面是包含了mId成员的Spreadsheet类定义:

class Spreadsheet
{
 public:
  int getId() const;

 protected:
  int mId;
  static int sCounter = 0;
  //static int sCounter;    // Pre C++11
}; 

代码取自 SpreadsheetDataMembers\Spreadsheet.h

下面是Spreadsheet构造函数的实现,在此赋予初始ID值:

Spreadsheet::Spreadsheet(int inWidth, int inHeight)
  : mWidth(inWidth),mHeight(inHeight)
{
  mId = sCounter++;

  mCells = new SpreadsheetCell* [mWidth];
  for (int i = 0; i < mWidth; i++) {
    mCells[i] = new SpreadsheetCell[mHeight];
  }
}

代码取自 SpreadsheetDataMembers\Spreadsheet.cpp

您己经看到,构造函数可以访问sCounter,就好像这是一个普通成员。

下面是对ID赋值的复制构造函数:

Spreadsheet::Spreadsheet(const Spreadsheet& src)
{
  mId = sCounter++;
  copyFrom(src);
}

代码取自 SpreadsheetDataMembers\Spreadsheet.cpp

在赋值运算符中不应该复制ID。一旦给ID指定了某个对象,就不应该再改变。

在方法外访问静态数据成员

访问控制限定符适用于静态数据成员:sCounter是protected,因此不能在类方法之外访问。如果scounter是公有的,就可以在类方法外访问,具体方法是用::作用域解析运算符指出这个变量是Spreadsheet类的一部分:

int c=Spreadsheet::sCounter;

然而,建议您不要使用公有数据成员。您应该提供公有get/set()方法来授予访问权限。如果想要访问静态的数据成员,应该实现静态的get/set方法,这些内容将在本章后面介绍。