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 // 由程序库客户建立
{
public:
Directory(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函数,可消除与初始化有关的“竞速形势”。