条款04:确定对象在使用前已经被初始化
读取未初始化的值会造成不明确的行为。
- 对于内置类型来说,我们必须手工完成初始化。
- 对于类类型来说,这个责任由构造函数来承担,所以构造函数要确保所有的数据成员有被初始化。
例如下面这个构造函数:
ABEntity::ABEntity(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
{
theName = name;
theAddress = address;
thePhones = phones;
}
注意这是我们新手比较容易犯的一个错误,我们以为上面这个构造函数对成员进行了初始化。其实,这不是初始化,这是赋值。虽然ABEntity可以得到你期望的值。
C++规定初始化的动作发生在进行构造函数本体之前。
比较好的一个做法是使用成员初始化列表:
ABEntity::ABEntity(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
: theName(name),
theAddress(address),
thePhones(phones){}
基于赋值的那个版本,首先调用default构造函数为theName,theAddress,thePhones设初始值,然后,在函数体内再进行赋值。所以说default构造函数所做的一切都没有用。
而基于成员初始值列表的做法是直接调用theName,theAddress,thePhones的copy构造函数。
对于大多数而言,第二种方式每个成员只调用一次copy构造函数的方式高效的多。而对于内置类型来说,采用成员初始化列表与在构造函数内进行赋值的代价是一样的,但为了统一,也把内置类型的初始化放在成员初始化列表。
问为什么内置类型的初始化,两种方式一样?
初始化是两个过程:内存分配,填写值。
对于第一种方式,在函数体内进行赋值,是先内存分配,再赋值。对于在成员初始化列表中初始化也是先分配内存,再赋值。两种作法的步骤都一样。
而类成员,第一种方式会导致调用默认构造函数无用功。