Effective C++

Chapter 1. 让自己习惯C++ (Accustoming Yourself to C++)

Item 4. 确定对象被使用前已先被初始化

(Make sure that objects are initialized before they're used.)

通常如果你使用 C part of C++ 而且初始化可能招致运行期成本,那么就不保证发生初始化。一旦进入 non-C part of C++, 规则有些变化。这就很好地解释了为什么 array (来自 C part of C++)不保证其内容被初始化,而 vector (来自 non-C part of C++)却有次保证。表面上这似乎是一个无法决定的状态,而最佳的处理办法就是:永远在使用对象之前先将它初始化

1. 对于无任何成员的内置类型,必须手工完成此事。例如:

int x = 0;                                 // 对 int 进行手工初始化
const char* text = "A C-style string"; //对指针进行手工初始化
double d;
std::cin >> d; //以读取 input stream 的方式完成初始化

2. 对于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。规则很简单:确保每一个构造函数都将对象的每一个成员初始化。这个规则很容易奉行,重要的是别混淆了赋值(assignment)和初始化(initialization)。考虑一个用来表现通讯簿的 class,其构造函数如下:

class PhoneNumber { ... };
class ABEntry //ABEntry = "Address Book Entry"
{
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; //这些都是赋值(assignment),
theAdress = address; //而不是初始化(initialization)。
thePhones = phones;
numTimesConsulted = 0;
}

这会导致 ABEntry 对象带有你期望(你指定)的值,但不是最佳做法。C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。在 ABEntry 构造函数内,theName, theAddress 和 thePhones 都不是被初始化,而是被赋值。初始化的发生时间更早,发生于这些成员的 default 构造函数被自动调用之时(比进入 ABEntry 的构造函数本体的时间更早)。但这对 numTimesConsulted 不为真,因为它属于内置类型,不保证一定在你所看到的那个赋值动作的时间点之前获得初值。

ABEntry 构造函数的一个较佳的写法是,使用所谓的 member initialization list (成员初值列)替换赋值动作

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

这个构造函数和上一个的最终结果相同,但通常效率较高。基于赋值的那个版本首先调用 default 构造函数为 theName, theAddress 和 thePhones 设初值,然后立刻再对它们赋予新值,default 构造函数的一切作为因此浪费了。成员初值列(member initialization list)的做法避免了这一问题,因为初值列中针对各个成员变量而设的实参,被拿去作为各成员变量之构造函数的实参。本例中的 theName 以 name 为初值进行 copy 构造,theAddress 以 address 为初值进行 copy 构造,thePhones 以 phones 为初值进行 copy 构造。

对于大多数类型而言,比起先调用 default 构造函数然后再调用 copy assignment 操作符,单只调用一次 copy 构造函数是比较高效的,有时甚至高效的多。对于内置型对象如 numTimesConsulted,其初始化和赋值的成本相同,但为了一致性最好也通过成员初值列来初始化。同样道理,甚至当你想要 default 构造一个成员变量,都可以使用成员初值列,只要指定无物作为初始化实参即可。假设 ABEntry 有一个无参数构造函数,可将它实现如下:

ABEntry::ABEntry( ) : theName(), theAddress(), thePhones(), numTimesConsulted(0)
{
//调用 theName, theAddress, thePhones 的default 构造函数
//记得将 numTimesConsulted 显示初始化为 0
}

记住总是在初值列中列出所有的成员变量,以免还得记住哪些成员变量(如果它们在初值列中被遗漏的话)可以无需初值。

有些情况下即使面对的成员变量属于内置类型(那么其初始化与赋值的成本相同),也一定得使用初值列。如果成员变量是 const 或 references,它们就一定需要初值,不能被赋值(见 Item 5)。为避免需要记住成员变量何时必须在成员初值列中初始化,何时不需要,最简单的做法就是:总是使用成员初值列。

许多 classes 拥有多个构造函数,每个构造函数有自己的成员初值列。如果这种 classes 存在许多成员变量和/或 base classes,多份成员初值列的存在就会导致不受欢迎的重复(在初值列内)和无聊的工作(对程序员而言)。这种情况下可以合理地在初值列中遗漏那些“赋值表现像初始化一样好”的成员变量,改用它们的赋值操作,并将那些赋值操作移往某个函数(通常为 private),供所有构造函数调用。这种做法在“成员变量的初值由文件或数据库读入”时特别有用。然而,比起经常由赋值操作完成的“伪初始化”(pseudo-initialization),通过成员初值列完成的“真正初始化”通常更加可取。

C++有着十分固定的“成员初始化次序”。是的,次序总是相同:base classes 更早于其 derived classes 被初始化(见 Item 12),而 class 的成员变量总是以其声明次序被初始化。回头看看 ABEntry,其 theName 成员永远最先被初始化,然后是 theAddress, 再来是 thePhones,最后是 numTimesConsulted。即使它们在成员初值列中以不同的次序出现(很不幸那是合法的),也不会有任何影响。因而在成员初值列中列各个成员时,最好以其声明次序为次序。<通俗来讲,两个成员变量的初始化带有次序性,例如初始化 array 时需要指定大小,因此代表大小的那个成员变量必须先有初值。>

一旦已经很小心地将“内置型成员变量”明确地加以初始化,而且也确保构造函数运用“成员初值列”初始化 base classes 和成员变量,那就只剩唯一一件事情需要操心,就是“不同编译单元内定义的 non-local static 对象”的初值化次序。

具体略。

请记住:

  • 为内置型对象进行手工初始化,因为 C++ 不保证初始化它们。
  • 构造函数最好使用成员初值列(member initialization list),而不要在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,其排列次序应该和它们在 class 中的声明次序相同。
  • 为免除“跨编译单元的初始化次序”问题,请以 local static 对象替换 non-local static 对象。

Effective C++ 之 Item 4:确定对象被使用前已先被初始化的更多相关文章

  1. [Effective C++ --009]确定对象被使用前已先被初始化

    在确保对象在使用前已先被初始化这一条款的编码实践中,作者为我们总结了三条经验,它们分别是: ------------------------------------------------------ ...

  2. EC读书笔记系列之2:条款4 确定对象被使用前已先被初始化

    条款4:确定对象被使用前已先被初始化 记住: ★为内置对象进行手工初始化,因为C++不保证初始他们 ★构造函数最好使用初始化列表,而不要在构造函数本体内使用赋值操作.初始化列表列出的成员变量,其排列次 ...

  3. Effective C++(4) 确定对象被使用前已先被初始化

    危害:读取未初始化的值会导致不明确甚至是半随机化的行为. 最佳处理办法:永远在使用对象之前先将它初始化:确保每一个构造函数都将对象的每一个成员初始化. 1 注意区分赋值和初始化: 从初始化的角度而言, ...

  4. Effective C++学习笔记 条款04:确定对象被使用前已先被初始化

    一.为内置类型对象进行手工初始化,因为C++不保证初始化它们. 二.对象初始化数据成员是在进入构造函数用户编写代码前完成,要想对数据成员指定初始化值,那就必须使用初始化列表. class A { pu ...

  5. Effective C++ 条款04:确定对象被使用前已经先被初始化

    规则一 永远在使用对象之前将它初始化 int x = 0; const char* text = "A C-style string"; double d; std:: cin & ...

  6. EffectiveC++条款04:确定对象被使用前已先被初始化

    不要混淆赋值和初始化,对于大多数类型而言,比起先调用默认构造函数然后调用赋值操作符,只调用一次拷贝构造函数是高效的 对于内置类型,也需要成员初值列(member initialization list ...

  7. [effictive c++] 条款04 确定对象被使用前已被初始化

    成员初始化 在c和c++ 中,使用为初始化的类型经常会引发不可预料的错误,从而使得我们要花费巨大的时间用于调试查找问题,所以确定对象被使用前已被初始化是个非常好的习惯. 永远在使用之前对对象进行初始化 ...

  8. 读书笔记 effective c++ Item 4 确保对象被使用前进行初始化

    C++在对象的初始化上是变化无常的,例如看下面的例子: int x; 在一些上下文中,x保证会被初始化成0,在其他一些情况下却不能够保证.看下面的例子: class Point { int x,y; ...

  9. 条款4:确定对象被使用前已被初始化(Make sure that objects are initialized before they're used)

    其实 无论学何种语言 ,还是觉得要养成先声明后使用,先初始化再使用. 1.永远在使用对象之前先将其初始化. 内置类型: 必须手工完成. 内置类型以外的:使用构造函数完成.确保每一个构造函数都将对象的一 ...

随机推荐

  1. ORM框架Entity Framework

    博客园在推广ORM方面的确做了很大的贡献,很多的程序员开始使用ORM,不用写SQL的喜悦让他们激动不已,可是好景不长,他们很快发现众多的烦恼一个接一个的出现了. 很遗憾,我并不打算在这篇文章中解决这些 ...

  2. STL---vector(向量)

    1 基本操作 (1)头文件#include<vector>. (2)创建vector对象,vector<int> vec; (3)尾部插入数字:vec.push_back(a) ...

  3. C#错误之 System.Threading.ThreadAbortException:正在中止线程

    参考:http://www.cnblogs.com/chendaoyin/archive/2013/06/27/3159211.html 1.开启一个子线程 //开启一个子线程,子线程调用方法 Met ...

  4. Python之调用WebService:suds

    suds官方: https://fedorahosted.org/suds/wiki/Documentation 互联网公开WSDL: http://www.webxml.com.cn/zh_cn/w ...

  5. appium + python 环境搭建

    所需:JDK.Android SDK.Appium服务程序.Appium客户端程序 1. 安装最新的JDK,并配置环境变量. JAVA_HOME=C:\Program Files (x86)\Java ...

  6. python读写文件时中文的转码问题

    读写文件都要将中文转为unicode字符. 读文件: u = unicode(s, 'gbk') 这里不能使用encode 写文件: u = encode('utf')

  7. pl/sql中having的用法

    HAVING的作用: 因为where关键字无法与聚集函数一起使用,HAVING可以和聚集函数一起使用 HAVING的语法: SELECT column_name, aggregate_function ...

  8. MFC 对话框添加菜单

    1.在Resource View 里右击菜单里选择Add Resource,选择menu,添加一个IDR_MENU1的菜单.在编辑器编辑菜单,添加菜单项,命名各个菜单项的ID. 2.在所要添加菜单的对 ...

  9. 欧洲杯 2016 高清直播 - 观看工具 UEFA-EURO-2016-Play.7z

    OnlineTV-MPlayer-nocache.exe 占 CPU 内存 较少 OnlineTV-FFPlay.exe 可截取图像 UEFA-EURO-2016-Play-v5.7z UEFA-EU ...

  10. 1.SpringMVC的简介和环境搭建

    SpringMVC的简介: SpringMVC 和 Struts一样是一个MVC框架,和Spring无缝连接,和struts2类似, Spring MVC属于SpringFrameWork的后续产品, ...