有时可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。如果成员const或是引用,则必须将其初始化。类似的,当成员属于某种类型并且该类没有定义默认构造函数时,也必将这个成员初始化。
class ConstRef {
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
和其他常量对象或者引用一样,成员ci和ri都必须被初始化。因此,如果我们没有为他们提供构造函数初始值的话将引发错误:
ConstRef::ConstRef(int ii)
{ // 赋值
i = ii; //正确
ci = ii; //错误:不能给const赋值
ri = i; //错误:ri没被初始化
}
随着构造函数体一开始执行,初始化就完成了。初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值,因此该构造函数的正确形式为:
ConstRef::ConstRef(int ii): i(ii), ci(ii), ri(i) { }
一般来说,初始化的顺序无特别要求。不过如果一个成员是用另一个成员来初始化,则两个成员的初始顺序则很关键。
class X {
int i;
int j;
public:
// 未定义的:i在j之前被初始化
X(int val): j(val), i(j) { }
};
X的构造函数写成如下形式效果会更好:
X(int val): i(val), j(val) { }
Sales_data默认构造函数的行为只与接收一个string实参的构造函数差不多。唯一的区别是接受string实参的构造函数使用这个实参初始化bookNo,而默认构造函数(隐式)使用string的默认构造函数初始化bookNo。我们可以把它们重写成一个使用默认实参的构造函数:
class Sales_data {
public:
// 定义默认构造函数,令其与只接受一个string实参的构造函数功能相同
Sales_data(std::string s = ""): bookNo(s) { }
// 其他构造函数与之前一致
Sales_data(std::string s, unsigned cnt, double rev):
bookNo(s), units_sold(cnt), revenue(rev*cnt) { }
Sales_data(std::istream &is) { read(is, *this); }
// 其他成员与之前的版本一致
};