EC读书笔记系列之2:条款4 确定对象被使用前已先被初始化
条款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 确定对象被使用前已先被初始化的更多相关文章
- EC读书笔记系列之14:条款26、27、28、29、30、31
条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...
- EC读书笔记系列之8:条款13、14、15
条款13 以对象管理资源 记住: ★为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放 ★两个常被使用的RAII classes分别是tr1::shared_ptr和aut ...
- EC读书笔记系列之16:条款35、36、37、38、39、40
条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...
- EC读书笔记系列之1:条款1、条款2、条款3
条款1:视C++为一个语言联邦 记住: ★C++高效编程守则视状况而变化,这取决于你使用C++的哪一部分 C: Object-oriented c++: Template c++: STL 条款2:尽 ...
- EC读书笔记系列之20:条款53、54、55
条款53 不要轻忽编译器的警告 记住: ★严肃对待编译器发出的警告信息.努力在你的编译器的最高(最严苛)警告级别下争取“无任何警告”的荣誉 ★不要过度依赖编译器的报警能力,∵不同的编译器对待事情的态度 ...
- EC读书笔记系列之19:条款49、50、51、52
条款49 了解new-handler的行为 记住: ★set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用 ★Nothrow new是一个颇为局限的工具,∵其只适用于内存 ...
- EC读书笔记系列之18:条款47、48
条款47 请使用traits classes表现类型信息 记住: ★Traits classes使得“类型相关信息”在编译期可用.它们以templates和“templates特化”完成实现 ★整合重 ...
- EC读书笔记系列之17:条款41、42、43、44、45、46
条款41 了解隐式接口与编译器多态 记住: ★classes和templates都支持接口和多态 ★对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函 ...
- EC读书笔记系列之15:条款32、33、34
条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...
随机推荐
- Android 基本控件
http://www.cnblogs.com/LT-blogs/archive/2012/08/07/2626118.html http://blog.csdn.net/android_tutor/a ...
- js正则判断电话/手机/邮箱/
用途:校验ip地址的格式 输入:strIP:ip地址返回:如果通过验证返回true,否则返回false:*/ function isIP(strIP) { if (isNull(strIP)) ret ...
- Servlet 浅谈(一)
Servlet 的生命周期 类加载.实例化,init方法,service方法,destroy方法.关于这点,后面会有详细介绍. 什么是容器? 因为Servlet没有main方法,所以它受控于另一个Ja ...
- AJAX远程跨域获取数据
//本地获取json文件 $.ajax({ url : 'json.php', type : 'post', dataType : 'json',//返回json数据格式 success : func ...
- 使用SecureCRT网络连接树莓派
为了更加方便可以通过网络来连接.控制树莓派,使用SecureCRT可以通过网络来连接树莓派. 1.在树莓派上通过终端命令ifconfig 来查看当前树莓派的IP地址: IP地址 ...
- UIButton基础知识
基本属性 1.frame;坐标:title:titlecolor:字体颜色:titleShadowColor:字体阴影:image:图片: backgroundImage:背景图片: 2.forsta ...
- Session和Cookie的关系
Session和Cookie关系 两者构建了web的回话数据 Cookie作为客户端的回话,Session为服务器端的 共同点: 都是1对1的,(一个客户一个独立的回话) 都以键值对的方式存储数据 都 ...
- python学习day2(二)
1.类与对象的关系 对于Python,一切事物都是对象,对象基于类创建 type是获取类的 dir是获取这个类里面的成员 2.int内部功能介绍 bit_length:返回表示当前数字占用的最少位数: ...
- ApiDemos示例学习(1)——ApiDemos示例的导入
---恢复内容开始--- 今天准备开始写这个ApiDemos示例的学习日记了,放在网上以监督自己! 首先是导入该示例.如果我们在配置Android开发环境是,利用Android SDK 安装包中的SD ...
- listview及adapter
http://blog.csdn.net/shaojie519/article/details/6595720 http://blog.csdn.net/liuhe688/article/detail ...