Effective C++ 条款04:确定对象使用前已先被初始化

前言

永远在使用对象之前先将它初始化。

赋值与初始化的区别

不要混淆赋值与初始化的区别

class PhoneNumber {...};
class ABEntry
{
public:
	ABEntry(const std::string &name, const std::string &address, const std::list<PhoneNumber> &phones);
private:
	std::string theName;
	std::string theAddress;
	std::list<PhoneNumber> thePhones;
	int numTimesConsulted;
};

ABEntry(const std::string &name, const std::string &address, const std::list<PhoneNumber> &phones)
{
	theName = name;        //这些都是赋值而非初始化。
	theAddress = address;
	thePhones = phones;
	numTimesConsulted = 0;
}

初始化发生的时间更早,发生于这些成员的default构造函数被调用之时(比进入构造函数的时间更早)。上述构造函数较佳的一个做法是,使用所谓的Member initialization list(成员初值列)替换赋值动作

ABEntry(const std::string &name, const std::string &address, const std::list<PhoneNumber> &phones):
		theName(name),  //这些都是初始化而非赋值。
		theAddress(ddress),
		thePhones(phones),
		numTimesConsulted(0)
{  }   //构造本体不必有任何动作

赋值的那个版本首先调用default构造函数为成员变量设初值,然后立刻再对他们赋予新值。成员初值列避免了这个问题,效率会高一些。

成员初始化次序

base classes更早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化。当你在成员初值列中条列各个成员时,最好总是以其声明次序为次序。

"定义于不同编译单元内的non-local static对象"初始化

函数内的static对象称为local static对象,其他 static对象称为non-local static对象。
C++对于 "定义于不同编译单元内的non-local static对象"初始化次序无明确定义。

class FileSystem{
public:
	std::size_t numDisks() const;
}
extern FileSystem tfs;

//另外一个文件,使用FileSystem
class Directory {
public:
	Directory(params);
};
Directory::Directory(params){
    ...
    std::size_t disks = tfs.numDisks();  //使用tfs对象
    ...
}

//使用
Directory tempDir;//为临时文件而做出的目录

初始化次序重要性:除非tfs在tempDir之前先被初始化,否则tempDir的构造函数会用到尚未初始化的tfs。但tfs和tempDir是不同的人在不同的时间于不同的源码文件建立起来的,它们是定义于不同编译单元内的non-local static对象。
决定它们的初始化次序相当困难,在其最常见形式,也就是多个编译单元内的non-local static对象经由“模板隐式具现化,implicit template instantiations”形成。
消除方法:
将每个non-local static对象搬到自己的专属函数内,这些函数返回一个reference指向它所含的对象。然后调用这个函数,而不直接指涉这些对象。换句话说,non-local static对象被local static对象替换了。

class FileSystem{...}
FileSystem &tfs()  //这个函数用来替换tfs对象,它在FileSystem class中可能是个static
{
    static FileSystem fs;  //定义并初始化一个local static对象
    return fs;			//返回一个reference指向上述对象
}
class Directory {...}
Directory::Directory(params){
    ...
    std::size_t disks = tfs().numDisks();
    ...
}
Directory &tempDir()   //同上
{
    static Directory td;
    return td;
}

为避免在对象初始化之前过早使用它们,你需要做三件事。第一,手工初始化内置型non-member对象。(int a = 5;).第二,使用成员初值列对付对象的所有成分。最后,在"初始化次序不确定性"氛围下加强设计。

总结

  • 为内置对象进行手工初始化,因为C++不保证初始化它们。
  • 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
  • 为免除“跨编译单元之初始化次序”问题,请以local static 对象替换 non-local static 对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值