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 ...
随机推荐
- twitter 监控登陆活动
http://vicenteaguileradiaz.com/download/tinfoleak/tinfoleak-1.2.tar.gz
- 【收藏】十大Webserver漏洞扫描工具
如今有很多消息令我们感到Web的危急性,因此,当前怎样构建一个安全的Web环境成为网管员和安全管理员们义不容辞的责任.可是巧妇难为无米之炊,该选择哪些安全工具呢? 扫描程序能够在帮助造我们造就安全的W ...
- 树形控件CTreeCtrl的使用
树形控件在界面编程中应用十分普遍,如在资源管理器中和树形结构显示书的文件夹等,我们一步步研究树形控件的使用. 在对话框界面上首先拖动创建一个树,一般我们改变三个属性: Has Buttons显示带有& ...
- jQuery 网格布局插件
如今,大多数网站设计要靠网格系统和布局,这能够提供给设计人员一个方便的途径来组织网页上的内容.网格的设计最常见于报纸和杂志的版面,由文字和图像构成的列组成. 这篇文章给大家分享精心挑选的15款最佳的 ...
- mma ctf 1st && csaw 2015
(很久以前做的,现在发一下)最近做了两个CTF,水平太渣,做了没几道题,挑几个自己做的记录一下. mma ctf 1st 之 rps: from socket import * s = socket( ...
- MySQL半同步Semi-sync原理介绍【图说】
上图先.
- WPF做的迁移文件小工具
客户这边需要往服务器上传PDF文件.然后PDF文件很多,需要挑出来的PDF文件也不少.因此做了个小工具. 功能很简单,选定源文件夹,选定记录着要提取的文件的excel 文件.OK ,界面如下. XAM ...
- math方法集合
math.abs(x) 返回数的绝对值.math.acos(x) 返回数的反余弦值.math.asin(x) 返回数的反正弦值.math.atan(x) 以介于 -PI ...
- filestream 读取视频文件
class Program { static void Main(string[] args) { string source = @"F:\361\android studio\4.0\亲 ...
- mysql优化(2) 观察mysql进程状态
环境准备,安装sysbench wget http://nchc.dl.sourceforge.net/project/sysbench/sysbench/0.4.12/sysbench-0.4.12 ...