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: ...
随机推荐
- Java面试题集(1-50)
下面的内容是对网上原有的面试题集及答案进行了全面修订之后的内容(原来的题目有很多重复无用的题以及错误的答案),参照了JDK最新版本,删除了重复题目以及EJB2.x等无用内容,补充最新面试题.知识点巩固 ...
- Handler没法取出消息队列中的数据的一个原因
主线程发送消息到工作线程,工作线程的步骤是固定为3步的. Looper.prepare();//步骤1,线程里使用handler必须这样写, handler = new Handler(){//步骤2 ...
- Core dotnet 命令大全
Core dotnet 命令大全 dotnet 命令大全,让你理解dotnet 命令. 本文将以一个实例串起 dotnet 所有命令,让你玩转dotnet 命令. 本篇文章编写环境为windows 1 ...
- ASP.NET MVC 5 学习教程:添加查询
原文 ASP.NET MVC 5 学习教程:添加查询 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控 ...
- 【HTTP 2】简介(Introduction)
前情提要 在上一篇文章<[HTTP 2.0] 序言>中,我们简要介绍了 HTTP 2 协议的概要和协议状态. 在本篇文章中,我们将会了解到 HTTP 2 协议简介(Introduction ...
- JSP动态网站环境搭建应用中的详细步骤(Tomcat和Apache/IIS的整合)
链接地址:http://www.cnblogs.com/dartagnan/archive/2011/03/25/2003426.html JSP动态网站环境搭建应用中的详细步骤(Tomcat和Apa ...
- SAX方式解析XML
sax解析分为以下几步: 1 获取一个saxparserfactory 2 获取一个解析器 3 创建handler对象,这个myHandler是继承了DefaultHandler的一个类,这个实现类里 ...
- WPF Multi-Touch 开发:高效开发模式
原文 WPF Multi-Touch 开发:高效开发模式 在前几篇文章中已经介绍了触屏操作的多种模式,并对其开发方式也有了进一步了解.细心的朋友应该会发现在上一篇文章中,如果拖动图片过快它会因惯性效果 ...
- jstl 保留两位小数
简介:jstl保留两位小数实现方法在JSP开头导入以下标签:<%@tagliburi="http://java.sun.com/jsp/jstl/fmt"pre jstl 保 ...
- Windows Azure 网站 (WAWS) 和中间证书
编辑人员注释:本文章由 Windows Azure 网站团队的项目经理 Erez Benari 撰写. 在 Windows Azure 网站上使用 SSL 已经司空见惯.虽然向网站上传和分配证书通常简 ...