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

记住:

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

  ★构造函数最好使用初始化列表,而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应和它们在class中的声明次序相同

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

-----------------------------------------------------------------------------------------------------------------------------------------------------------

1 区别赋值初始化

举例:

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

  而C++规定对象的成员变量的初始化发生在进入构造函数本体之前。改进如下:

 ABEntry::ABEntry( const std::string &name, const std::string &address,
const std::list<PhoneNumber>& phones
)
:theName(name), //现在这些都是初始化
theAddress(address),
thePhones(phones),
numTimesConsulted()
{ } //构造函数本体无需任何动作

这样效率高的原因:

  第一个版本首先需调用default构造函数为theName等设初值,然后再立刻对它们赋新值。default构造函数的一切作为因此浪费了。而版本二初始化列表中的各个实参被拿去作为各成员变量之构造函数的实参。

  内置型对象其初始化和赋值成本一样,但为了一致性最好也通过初始化列表来初始化。

当欲要default构造一个成员变量,甚至都可以使用初始化列表,只要指定无物即可,如下:

 ABEntry::ABEntry( const std::string &name, const std::string &address,
const std::list<PhoneNumber>& phones
)
:theName( ), //现在这些都空,会调用theName的default构造函数,下同
theAddress( ),
thePhones( ),
numTimesConsulted() //记得将内置类型显式初始化为0
{ } //构造函数本体无需任何动作

有时可以合理地在初始化列表中遗漏那些“赋值表现像初始化一样好”的成员,改用它们的赋值操作,并将那些赋值操作移往某个函数(通常是private),供所有构造函数(若有多个构造函数的话)调用。此做法在“成员变量的初值系有文件或数据库读入”时特别有用。

C++成员初始次序很固定:base classes早于derived classes;成员变量以声明次序被初始化。

2 不同编译单元内定义之non-local static对象的初始化次序问题

一点一点来解释:

① 所谓static对象包括(3类):

  global对象;

  定义于namespace作用域内的对象;

  在classes内,函数内,以及在file作用域内被声明为static的对象

注:函数内的static对象成为local static对象,其他static对象称为non-local static对象

② 所谓编译单元:

  产出单一目标文件的那些源码。基本上它是单一源码文件加上其所含入的头文件。

举例:

 class FileSystem {  //来自你的程序库
public:
...
std::size_t numDisks() const; //众多成员函数之一
...
};
extern FileSystem tfs; //预备给客户使用的对象

若用户建立了一个class用来处理文件系统内的目录,很自然其class会用上FileSystem对象:

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

进一步假设,这些客户决定创建一个Directory类型的对象:

Directory tempDir( params );

现在初始化次序的重要性显现出来了:除非tfs先于tempDir被初始化。而C++对“定义于不同编译单元内的non-local static对象”的初始化相对次序并无明确定义!!!

如下设计可消除上面问题:

  将每个non-local static对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。然后用户调用这些函数,而不直接指涉这些对象。这个技巧基础在于:c++保证,函数内的local static对象会在:“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。所以若你以“函数调用”(返回一个reference指向local static对象)替换“直接访问non-local static对象”,你就获得了保证!!!

改进后程序如下:

 class FileSystem {...};     //同前
FileSystem& tfs() { //用此函数来替换tfs对象。该函数在
//FileSystem类中可能是个static。这类函数称为 //reference-returning函数,适合写成inline函数,下同 static FileSystem fs; //local static对象!!!
return fs;
} class Directory {...}; //同前
Directory::Directory( params ) { ...
std::size_t disks = tfs().numDisks(); //函数来替换对象!!!
...
} Directory& tempDir() { //此函数用来替换tempDir对象。该函数在
//Directory类中可能是个static static Directory td;
return td;
}

EC读书笔记系列之2:条款4 确定对象被使用前已先被初始化的更多相关文章

  1. EC读书笔记系列之14:条款26、27、28、29、30、31

    条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...

  2. EC读书笔记系列之8:条款13、14、15

    条款13 以对象管理资源 记住: ★为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放 ★两个常被使用的RAII classes分别是tr1::shared_ptr和aut ...

  3. EC读书笔记系列之16:条款35、36、37、38、39、40

    条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...

  4. EC读书笔记系列之1:条款1、条款2、条款3

    条款1:视C++为一个语言联邦 记住: ★C++高效编程守则视状况而变化,这取决于你使用C++的哪一部分 C: Object-oriented c++: Template c++: STL 条款2:尽 ...

  5. EC读书笔记系列之20:条款53、54、55

    条款53 不要轻忽编译器的警告 记住: ★严肃对待编译器发出的警告信息.努力在你的编译器的最高(最严苛)警告级别下争取“无任何警告”的荣誉 ★不要过度依赖编译器的报警能力,∵不同的编译器对待事情的态度 ...

  6. EC读书笔记系列之19:条款49、50、51、52

    条款49 了解new-handler的行为 记住: ★set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用 ★Nothrow new是一个颇为局限的工具,∵其只适用于内存 ...

  7. EC读书笔记系列之18:条款47、48

    条款47 请使用traits classes表现类型信息 记住: ★Traits classes使得“类型相关信息”在编译期可用.它们以templates和“templates特化”完成实现 ★整合重 ...

  8. EC读书笔记系列之17:条款41、42、43、44、45、46

    条款41 了解隐式接口与编译器多态 记住: ★classes和templates都支持接口和多态 ★对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函 ...

  9. EC读书笔记系列之15:条款32、33、34

    条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...

随机推荐

  1. Hibernate征途(三)之CRUD

    上篇博客<Hibernate征途(二)之基础与核心>介绍了Hibernate的基础内容和核心内容,这篇博客简单实践一下.第一篇博客也说过Hibernate是一种JDBC的简化方案,既然是和 ...

  2. C# 整个网页保存成图片

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  3. 关于常用meta的总结

    入行也半年了,无数次的想过写博客也无数次的想过第一篇会写什么,一直没有落实.今天心血来潮把博客开了,那就写点东西吧.第一篇就写一写看似简单但又经常不注意到的meta标签吧.(博主经验尚浅,有许多理解不 ...

  4. CalendarHelper日期计算工具,各种日期的获取和计算

    今天分享一个日期获取和计算的工具类,这个最初是用在项目中获取每周每月日期用的. <Attention> 之后逛帖子的时候发现了这个专门处理日期的库,java上也有,android上还为了避 ...

  5. 读书笔记-实用单元测试(英文版) Pragmatic Unit Testing in C# with NUnit

    读书笔记-实用单元测试(英文版) Pragmatic Unit Testing in C# with NUnit Author: Andrew Hunt ,David Thomas with Matt ...

  6. Qt for Windows:Qt 5.4.0 MinGW 静态编译版本制作 (转)

    大致流程: 1.安装Qt(源码版)以及其他必要的环境 2.编译/安装 3.配置 4.使用 ----------正文分割线---------- 1.安装Qt(源码版) 1.1 下载Qt(两个地址二选一即 ...

  7. 16-GDBT(MART) 迭代决策树入门教程 | 简介

    转载:http://blog.csdn.net/w28971023/article/details/8240756 GBDT(Gradient Boosting Decision Tree) 又叫 M ...

  8. java中等于和equals的区别

    面试的时候没答上,很受打击,特写在这里. ==是判断两个变量或实例是不是指向同一个内存空间equals是判断两个变量或实例所指向的内存空间的值是不是相同 除了String和封装器,equals()和“ ...

  9. JavaScript初学者应知的24条最佳实践(译)

    原文:24 JavaScript Best Practices for Beginners 译者:youngsterxyf (注:阅读原文的时候没有注意发布日期,觉得不错就翻译了,翻译到JSON.pa ...

  10. docker 在esx上的网络配置