Effective C++_笔记_条款04_确定对象被使用之前已先被初始化
(整理自Effctive C++,转载请注明。整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/)
读取未初始化的值会导致不确定的行为。在某些平台上,仅仅只是读取为初始化的值,就有可能让你的程序终止运行。更可能的情况是读入一些“半随机”bits,污染了正在进行读取的那个对象,最终导致不可测知的程序行为,以及令人不愉快的调试过程。
提出最佳准则:永远在使用对象之前将它初始化。分为两部分:对于无任何成员的内置类型,你必须手工完成此事;至于内置类型以外的其他任何东西,初始化责任落在构造函数身上,规则:确保每一个构造函数都将对象的每一个成员初始化。
这个规则很容易奉行,有两点需要注意:
1 赋值和初始化的区别
考虑一个用来表现通讯簿的class,其构造函数如下:
1: class PhoneNumber{...};
2: class ABEntry{
3: public:
4: ABEntry ( const string& name , const string& address ,
5: const list<PhoneNumber>& Phones ) ;
6:
7: private:
8: string theName ;
9: string theAddress ;
10: list<PhoneNumber> thePhones ;
11: int numTimeConsulted ;
12: };
13:
14: ABEntry::ABEntry( const string& name , const string& address ,
15: const list<PhoneNumber>& Phones )
16: {
17: theName = name ; //这些都是赋值,而非初始化
18: theAddress = address ;
19: thePhones = phones ;
20: numTimeConsulted = 0 ;
21: }
这会导致ABEntry对象带有你期望的值,但不是最佳做法。C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。在ABEntry的构造函数内,theName、theAddress和thePhones都不是被初始化,而是被赋值。初始化发生的时间更早,发生于这些成员的default构造函数被自动调用之时(比进入ABEntry的构造函数本体的时间更早)。但这对numTimeConsulted不为真,因为它属于内置类型,不保证一定在你看到的那个赋值动作的时间点之前获得初值。ABEntry的构造函数的一个较佳的写法是,使用所谓的member initialization list(成员初值列)替换赋值动作:
1: ABEntry::ABEntry ( const string& name , const string& address ,
2: const list<PhoneNumber>& phones )
3: :theName(name), //现在,这些都是初始化
4: theAddress(address),
5: thePhones(phones),
6: numTimeConsulted(0)
7: {}
这个构造函数和上一个的最终结果相同,但通常效率更高。基于赋值的那个版本首先调用自定义类型成员变量的default的构造函数为它们设初值,然后立刻再对它们赋予新值。default构造函数的一切作为因此浪费了。成员初值列的做法避免了这一问题,因为初值列中针对各个成员变量而设的实参,被拿去作为各个成员变量之构造函数的实参,调用copy构造函数。
对于大多数类型而言,比起先调用default构造函数然后再调用copy assignment操作符,单只调用一次copy构造函数是比较高效的,有时甚至高效很多。对于内置类型,其初始化和赋值的成本相同,但为了一致性最好也通过成员初值列来初始化。同样道理,甚至当你想要default构造一个成员变量,你都可以使用成员初值列,只要指定无物作为初始化实参即可。请立下一个规则:规定总是在初值列中列出所有变量,以免还要记住哪些成员变量无需初值。
有些情况下即使成员变量属于内置类型,也一定要使用成员初值列。是的,如果成员变量是const或reference,它们就一定需要初值,不能被赋值。为避免需要记住成员变量何时必须在成员初值列中初始化,何时不需要,最简单的做法就是:总使用成员初值列。这样做有时绝对必要,且又往往比赋值更高效。
许多classes拥有多个构造函数,每个构造函数有自己的成员初值列。如果这种classes存在许许多多的成员变量和/或base classes,多份成员初值列的存在就会导致不受欢迎的重复和无聊的工作。这种情况下可以合理地在初值列中遗漏那些“赋值表现像初始化一样好”的成员变量,改用赋值操作,并将那些赋值操作移往某个函数(通常private),供所有构造函数调用。这种做法在“成员变量的初值系由文件或数据库读入”时特别有用。
另外,C++有着十分固定的“成员初始化次序”:base class更早于derived class被初始化,而class的成员变量总是以其声明次序被初始化。
2 “不同编译单元内定义之non-local static对象”的初始化次序
我们的问题是:如果某编译单元内的某个non-local static 对象的初始化动作使用了另一编译单元内的某个non-local static 对象,它所使用到的这个对象可能未被初始化,因为C++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确的定义。
书上有个例子,大家可以看看。幸运地是一个小小的设计便可完全消除这个问题。唯一需要做的是:将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。然后用户调用这些函数,而不直接指涉这些对象。换句话说,non-local static对象被local static对象替换了。
这个手法的基础在于:C++保证,函数内的local static 对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。所以你如果以“函数调用”替换“直接访问non-local static 对象”,你就获得了保证,保证你获得的那个reference将指向一个历经初始化的对象。
请记住:
(1)为内置类型进行手工初始化,因为C++不保证初始化它们。
(2)构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中声明的次序相同。
(3)为免除“跨编译单元之初始化次序”问题,以local static对象替换non-local static对象。
Effective C++_笔记_条款04_确定对象被使用之前已先被初始化的更多相关文章
- EC读书笔记系列之2:条款4 确定对象被使用前已先被初始化
条款4:确定对象被使用前已先被初始化 记住: ★为内置对象进行手工初始化,因为C++不保证初始他们 ★构造函数最好使用初始化列表,而不要在构造函数本体内使用赋值操作.初始化列表列出的成员变量,其排列次 ...
- [Effective C++ --009]确定对象被使用前已先被初始化
在确保对象在使用前已先被初始化这一条款的编码实践中,作者为我们总结了三条经验,它们分别是: ------------------------------------------------------ ...
- Effective C++ 之 Item 4:确定对象被使用前已先被初始化
Effective C++ Chapter 1. 让自己习惯C++ (Accustoming Yourself to C++) Item 4. 确定对象被使用前已先被初始化 (Make sure th ...
- Effective C++_笔记_条款12_复制对象时勿忘其每一个成分
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 编译器会在必要时候为我们的classes创建copying函数, ...
- Effective C++_笔记_条款02_尽量以const、enum、inline替换#define
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 这个条款或许改为“宁可以编译器替换预处理器”比较好,因为或许#d ...
- Effective C++_笔记_条款01_视C++为一个语言联邦
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++的各种能力和特性使它成为一个无可匹敌的工具,但也可能引发某 ...
- Effective C++阅读笔记_条款2:尽量以const,enum,inline替换#define
1.#define缺点1 #define NUM 1.2 记号NUM可能没有进入记号表,在调试或者错误信息中,无法知道1.2的含义. 改善:通过const int NUM = 1.2; 2.#dein ...
- Effective C++_笔记_条款11_在operator=中处理“自我赋值”
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为什么会出现自我赋值呢?不明显的自我赋值,是“别名”带来的结果: ...
- Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为方便采用书上的例子,先提出问题,在说解决方案. 1 问题 1: ...
随机推荐
- Visual Studio 2015编译安装配置QT5.5.1(含QTWEBKIT)
尽管QT5.5.1和VisualStudio 2015都已经发布很久了,但是QT项目组视乎不会为QT5.5.1专门发布预编译的QT5.5.1 for windows(2015)版本的,也不会专门发布V ...
- 基于visual Studio2013解决C语言竞赛题之0525拉丁方阵
题目
- DHTML【11】--DOM
大家好,从今天开始,我们将进入DOM的学习. DOM?DOM是何东东呢?大家还记得我在前面提过的DOM树吗?就是我在前面讲HTML的时候画的那个图,那个其实就是一个简单的DOM树,浏览器在解析HTML ...
- 关于Cocos2d-x 3.0正式版 粒子问题在IOS上正常显示,在Android下有问题的解决方式
前几个在Cocos2d-x论坛上,有人提到粒子系统的问题..这里列举一下解决的方法: 或许到时候大家用粒子效果的时候也会发现这个问题,如今把这个问题的解决办法说出来.至于原因我也不知道是引擎的问题还是 ...
- 基于JSP+SERVLET的新闻发布系统(三)
拖了这么久..今天把栏目管理还有新闻管理模块的也挂出来.. 栏目管理跟用户管理一样. 这里重点讲解新闻管理. 效果图如上: 1,可选择栏目类别,且栏目类别是动态生成的. 默认生成的文章是未审核状态的. ...
- 2008r2 做windows域控制器
新配一个: 1.装DNS服务. 2.装domain管理. config domain: 客户端172.16.1.34 ping zyctest
- AdbWinApi编译详解(本人亲历)
1. 从微软官方下载WDDK,比如:GRMWDK_EN_7600_1.ISO(http://download.microsoft.com/download/4/A/2/4A25C7D5-EFBE-41 ...
- BZOJ 1047: [HAOI2007]理想的正方形( 单调队列 )
单调队列..先对每一行扫一次维护以每个点(x, y)为结尾的长度为n的最大最小值.然后再对每一列扫一次, 在之前的基础上维护(x, y)为结尾的长度为n的最大最小值. 时间复杂度O(ab) (话说还是 ...
- 幻世(OurDream)2D图形引擎使用教程11——播放媒体文件(1)
声明:本教程版权归Lizcst Software Lab所有,欢迎转载,但是转载必须保留本段声明文字,并注明文章来源:http://blog.csdn.net/kflizcst 谢谢合作! 播放媒体是 ...
- mysql事务和锁InnoDB(转)
背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备 ...