条款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. Oracle日志文件常用操作

    Oracle关于日志文件基本操作1.查询系统使用的是哪一组日志文件:select * from v$log; 2.查询正在使用的组所对应的日志文件:select * from v$logfile; 3 ...

  2. 【并查集+拓扑排序】【HDU1811】【Rank of Tetris】

    题意:给你3种关系 A=B,A>B,A<B 问是否排名方式唯一,或者存在矛盾 解 1.读入数据先处理 =号 用并查集的祖先作为代表元素,其他儿子节点都等于跟这个点重叠. 再读入 '< ...

  3. 绿色mysql启动脚本

    启动脚本如下:./mysqld_safe --defaults-file=/export/servers/mysql-5.5.38/my.cnf --ledir=/export/servers/mys ...

  4. ASP.NET MVC 学习之路-5

    本文在于巩固基础 数据库开发模式: 1.数据库优先开发模式 2.模型优先开发模式 EntityFramework学习之一 最简单的一个案例 第一步创建模型 public class Student { ...

  5. .net控件dropdownlist动态绑定数据 ----转

    DropDownList控件的使用(数据绑定)(.net学习笔记二) 一.在页面初始化时候将集合绑定到DropDownListpublic void Page_Load(Object src.Even ...

  6. asp.net 获取IP地理位置的几个主要接口

    腾讯的IP地址API接口地址:http://fw.qq.com/ipaddress 新浪的IP地址查询接口:http://int.dpool.sina.com.cn/iplookup/iplookup ...

  7. androik_sdk 更新慢问题解决办法。

    在windows中更改hosts(找到文件夹C:\Windows\System32\drivers\etc找到里面的hosts文件 )在这里添加74.125.237.1 dl-ssl.google.c ...

  8. slice,substr,substring

    var s = '0123456789';   //slice 遇负数,则此负数值等同于 s.length + (负数) s.slice(3,6); //"345", 从位置3开始 ...

  9. JavaScript 对象扩展代码

    JavaScript 扩展代码 更具需要写的几个扩展. 扩展核心自执行函数 Object.extend /** * 对象扩展体 参数是 {属性|方法:属性值|方法体} * 只执行实现 * * 实例对基 ...

  10. JDK安装与环境变量配置(Win7)

    1.下载JDK(Java SE Development Kit) 地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-dow ...