C++开发初级


954 浏览 5 years, 4 months

3.1.5 构造函数初始化器

版权声明: 转载请注明出处 http://www.codingsoho.com/
构造函数初始化器

本章内容到现在为止,都是在构造函数体内初始化数据成员,例如:

SpreadsheetCell::SpreadsheetCell()
{
  mValue = 0;
  mString = "";
}

C++提供了另一种在构造函数中初始化数据成员的方法,叫做构造函数初始化器或者ctor-initializer。下面的代码使用ctorinitializer语法重写了没有参数的SpreadsheetCell构造函数:

SpreadsheetCell::SpreadsheetCell() : mValue(0), mString("")
{
}

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCellInitList.cpp

正如您看到的那样,ctor-initializer出现在构造函数参数列表以及构造函数体的开花括号之间。这个列表以冒号开始,由逗号分割。列表中的每个元素都使用函数符号或者调用超类构造函数初始化了某个数据成员。

使用ctor-initializer初始化数据成员与在构造函数体内初始化数据成员不同。当C++创建某个对象时,必须在调用构造函数前创建对象的所有数据成员。如果数据成员本身就是对象,那么在创建这些数据成员时,必须为其调用构造函数。当在构造函数体内给某个对象赋值时,并没有真正创建这个对象,而只是改变对象的值。(注:注意创建和赋值的差别,赋值时必须对象已存在)

ctor-initializer允许在创建数据成员的时候赋初值,这样做比在后面赋值效率高。有趣的是, string的默认初始化值是空字符串,因此前面的示例中显式地将mString初始化为空字符是多余的。

如果类中的数据成员是没有构造函数的对象,必须使用ctor-initializer正确地构建这个对象。例如,考虑下面的SpreadsheetCell类:

class SpreadsheetCell
{
 public:
  SpreadsheetCell(double d);
};

这个类只有一个显式采用double做参数的构造函数,而没有默认构造函数。可在另一个类中将这个类用作数据成员,如下所示:

class SomeClass
{
 public:
  SomeClass();
 protected:
  SpreadsheetCell mCell;
};

SomeClass构造函数的某个实现如下:

SomeClass::SomeClass(){}

然而,使用这个实现将无法成功编译下面的代码行。编译器不知道如何初始化SomeClass类的mCell数据成员,因为这个类没有默认构造函数。

SomeClass s;

解决方案是在ctor-initializer中初始化mCell数据成员,如下所示:

SomeClass::SomeClass():mCell(1.0){}

ctor-initializer允许在创建数据成员的时候执行初始化。

某些程序员喜欢在构造函数体内通过赋值提供初始值。然而,某些数据类型必须在ctor-initializes中初始化。下表对此进行了总结。

数据类型 说明
const数据成员 当const变量创建后无法对其正确赋值。必须在创建的时候提供值
引用数据成员 如果不指向一个量,引用将无法存在
没有默认构造函数的对象数据成员 C++尝试用默认构造函数初始化成员对象。如果不存在默认构造函数,就无法初始化这个对象
没有默认构造函数的超类 在第8章讲述

关于ctor-initializer要特别注意:初始化数据成员的顺序是按照在类定义中出现的顺序,而不是在ctor-initializer中的顺序。考虑下面的SpreadsheetCell类定义:

class SpreadsheetCell
{
 protected:
  double mValue;
  string mString;
};

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCell.h

假定SpreadsheetCell字符串构造函数使用了ctor-initializer,如下所示:

SpreadsheetCell::SpreadsheetCell(string initialValue) :
  mString(initialValue), mValue(stringToDouble(mString))    // INCORECT ORDER!
{
}

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCellInitListBackward.cpp

尽管该代码可以编译(有些编译器会给出警告),但是程序无法正确运行。您可能假定mString会在mValue之前初始化,因为mString在ctor-initializes中位列第一,但是C++并不是这么运行的。

C++弱类型,可以编译但运行时错误

SpreadsheetCell类中mValue的声明在mString之前,因此ctor-initializes会试图在mString之前初始化mValue。但是这段代码试图用mString的值初始化mValue,而mString尚未被初始化!

在此情况下的解决方案是使用initialValue参数而不是mString初始化mValue。为了避免混淆,还应该在ctor-initializer中交换顺序:

SpreadsheetCell::SpreadsheetCell(string initialValue) :
  mValue(stringToDouble(initialValue)), mString(initialValue)
{
}

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCellInitList.cpp

提示: ctor-initializer初始化数据成员的顺序是按照类定义中声明的顺序而不是ctorinitializer列表中的顺序。