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

Effective C++学习笔记总链接

改善程序与设计的55个具体做法学习笔记-每日1条


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

【技巧】:

1. 为内置型对象进行手工初始化,因为C++不保证初始化他们。

2. 构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。

3. 初始化列表的成员变量,其排列次序应该和他们在class中声明次序相同。

4. 为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static 对象


读取未初始化的值会导致不明确的行为

永远在使用对象之前先将它初始化,对于无任何成员的内置类型,必须手工完成此事。

至于内置类型之外的任何其他东西,确保每一个构造函数都将对象的每一个成员初始化

构造函数最佳写法,使用初始化列表

// 构造函数的初始化列表
ABEntry::ABEntry(const std::string& name, const std::string& address,
	const std::list<PhoneNumber>& phones)
	:theName(name),theAddress(address),thePhones(phones), numTimesConsulted(0)
	{
     }		

构造函数的初始化列表和构造函数内赋值,效率更高
对于大多数类型而言,比起先调用default构造函数然后再调用 copy assignment 操作符(构造函数内赋值单只调用一次copy构造函数(初始化列表) 是比较高效的,有时甚至高效得多。
对于内置数据类型,其初始化和赋值成本相同,但为了一致性最好也通过成员初始化列表初始化

总在初始化列表中列出所有成员变量

有些情况下,必须使用初始化列表,不能使用赋值。例如 成员变量为const或references.

总是使用初始化列表,有时候绝对必要,且又往往比赋值更高效

对于许多class拥有多个构造函数

多份成员初始化列表的存在就会导致重复和无聊的工作。

可以合理地在初始化列表中遗漏那些“赋值表现像初始化一样好”的成员变量,改用他们的赋值操作,并将那些赋值操作移往某个函数(通常为private),供所有的构造函数调用。

class 的成员变量总是以其声明次序被初始化

即使它们在成员初始化列表中以不同的次序出现,也不会影响以声明次序初始化成员变量

当你在成员初始化列表中条列各个成员时,最好总是以其声明次序为次序

local static 代替 non-local static,避免不同编译单元初始化次序问题

编译单元是指产出单一目标文件的那些源码,基本是它是单一源码文件加上所含的头文件

class FileSystem // 来自你的程序库
{
     
public:
	...
	std::size_t numDisk() const;
	...
};
extern FileSystem tfs; //预备给客户使用的对象

class Directory // 由程序库客户建立
{
     
publicDirectory(params); 
	...
};
Directory::Directory(params)
{
     	
	std::size_t disk = tfs.numDisk(); // 使用tfs对象
	...
}

Directory tempDir(params); // 为临时文件而做出的目录

上述程序问题: 除非tfs在tempDir之前先被初始化,否则tempDir的构造函数会用到尚未初始化的tfs。

C++ 对定义于不同的编译单元内的non-local-static对象的初始化相对次序无明确定义。不可能决定正确的初始化次序。无法保证non-local-static对象被其他编译单元调用前被初始化

解决办法:将每个non-local static 搬到自己专属函数内(该对象在此函数内被声明为static),这些函数返回一个reference指向所含的对象。调用这些函数,而不直接指涉这些对象。

class FileSystem {
      ... }; // 同前
FileSystem& tfs() //该函数替换tfs对象,reference-returning函数
{
     
	static FileSystem fs;
	return fs;
}

class Directory {
      ... }; // 同前
Directory::Directory(params)
{
     	
	std::size_t disk = tfs().numDisk(); // 调用tfs()
	...
}

Directory& tempDir(params) //该函数替换tempDir对象,reference-returning函数
{
     	
	static Directory td;
	return td;
}

C++保证,函数内的local static对象会在“该函数被调用期间” “首次遇上该对象之定义式”时被初始化。

所以以“函数调用”(返回一个reference指向local static对象)替换“直接访问non-local static对象

从另一个角度讲,函数内含“static 对象”使他们在多线程系统中带有不确定性
解决办法:在程序的单线程启动阶段手工调用所有的reference-returning函数,可消除与初始化有关的“竞速形势”。

总结:为了避免对象初始化之前使用它们,需要做到三点:

1. 手工初始化内置型non-member对象

2. 使用成员初始化列表对付对象的所有成员变量

3. 在“初始化次序不确定性”(针对不同编译单元所定义的non-local static),使用reference-returning 函数

你可能感兴趣的:(c++,Effective,C++,学习笔记,c++)