条款04:确定对象被使用前已被初始化

条款04:确定对象被使用前已被初始化

Make sure that object are initialized befor they’re used.

What

C++的语法规定:

1. C++不保证初始化内置型对象。

2. 对于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。

3. C++有着十分固定的“成员初始化次序”。Base classes 更早于其derived classes 被初始化,而class的成员变量总是以其声明次序被初始化。详细:基类首先被初始化(如果存在的话,如果是多重继承,那先初始化虚继承,然后再依次初始化),然后就是依据声明的次序依次初始化,如果初始化列表中存在定义,则调用初始化列表中的方法进行初始化,如果没有在初始化列表中初始化,那对于基本类型就根据编译器而定,类对象则调用其默认构造函数进行初始化(如果存在的话,如果不存在就必须在初始化列表中初始化)

4. 成员变量是 const 或 references,它们就一定需要初值,不能被赋值。

5. C++对于“定义跨编译单元内的non-local static对象”的初始化相对次序并无明确定义。

6. C++中的static对象是指存储区不属于stackheap"寿命"从被构造出来直至程序结束为止的对象。这些对象包括全局对象,定义于namespace作用域的对象,在classfunction以及file作用域中被声明为static的对象。其中,函数内的static对象称为local static 对象,而其它static对象称为non-local static对象。

7. 对于local static对象,在其所属的函数被调用之前,该对象并不存在,即只有在第一次调用对应函数时,local static对象才被构造出来。而对于non-local static对象,在main()函数开始前就已经被构造出来,并在main()函数结束后被析构,例子祥见最后一页代码。

Why

Ø 读取未初始化的值会导致不明确的行为。在某些平台上,仅仅只是读取未初始化的值,就可能让你的程序停止与你运行。更可能的情况是读入一些“伴随机”bits,污染了正在进行读取动作的那个对象,最终导致不可测知的程序行为,以及许多令人不许快的调试过程。

Ø 由于进入构造函数本体之后并非初始化,因此构造函数体内的对成员变量的赋值,实际上是伪初始化(pseudo-initialization),伪初始化效率低下:如果没有提供初始化列表,那么进入构造函数体之前,各成员变量已经调用默认构造函数初始化(自定义类型)或由编译器决定(内置类型对象的初始化),函数体内再赋值。

Ø 初始化列表中:成员出现的次序即使不是声明的次序,也不会改变初始化默认的初始化顺序:按声明次序初始化class member。如果不按照声明次序写,会引起难以理解的错误(多个成员变量的初始化带有次序性)

例如:

初始化数组时需要指定大小,因此代表大小的那个变量必须先初始化。

Ø 由于what中的第五条引发了一个问题:如果某编译单元内的某个non-local static 对象的初始化动作使用了另一个编译单元内的某个non-local static对象,它所用到这个对象可能尚未被初始化,这就带来不明确的行为。所以必须解决相互依赖的类对象间的初始化。

How

1. 为避免需要记住变量何时需要初始化,何时不需要初始化,最简单的做法就是:总是初始化。

2. 为内置型对象进行手工初始化。

3. 构造函数最好使用成员初始化列表(member initialization list),而不要再构造函数本体内使用赋值(assignment).

4. 初始化列表中成员初始化顺序遵循声明的顺序,避免晦涩的错误。

5. 由于what中的第7点,我们将每一个non-local static对象放到自己的专属函数内(该对象在此函数内被声明为static),这些函数返回一个reference指向它所含的对象。将“函数调用”返回一个reference指向local static对象替换“直接访问non-local static对象”。你就保证了用到该对象时,它已被初始化,而且从未调用是,还不会引发构造函数成本(non-local static却不是这样高效)。这在模式设计中是Singleton(单例)模式的一种常见手法。

6. 上点中local static reference-returning函数往往结构就是这么简单(看下面图片例子):第一行定义并初始化一个local static对象,第二行就返回它。在被频繁调用的时候,inline的修饰是最佳的选择。

7. 在多线程模式下,任何一种non-const static对象,不论是local还是non-local,等待某事的发生都会有麻烦。处理这个麻烦的一种做法:在程序的单线程启动阶段(single-threaded startup portion)手工调用所有上一点的函数,这可消除与初始化有关的“竞速形式(race conditions)”。


 

#include <iostream>
using namespace std;
class LocalStaticClass 
{
public:
    LocalStaticClass(){cout<<"Local static object constructed"<<endl;}
    ~LocalStaticClass(){cout<<"Local static object destructed"<<endl;}
 
};
class ClassInnerClass 
{
public:
    ClassInnerClass(){cout<<"Class inner  object constructed"<<endl;}
    ~ClassInnerClass(){cout<<"Class inner object destructed"<<endl;}
};
class FileStaticClass 
{
public:
    FileStaticClass(){cout<<"File Static object constructed"<<endl;}
    ~FileStaticClass(){cout<<"File Static object destructed"<<endl;}
 
};
class WrapperClassA
{
 public:
    WrapperClassA(){}
    
    LocalStaticClass & singleton()
    {
    	static LocalStaticClass LocalStaticObj;   //local static object
    	return LocalStaticObj;
    }
};
// class with non-local static object
class WrapperClassB
{
public:
    WrapperClassB(){ }
    ~WrapperClassB(){ }
//static data member declaration
    static ClassInnerClass ClassInnerObjB;//static声明 
};
ClassInnerClass WrapperClassB :: ClassInnerObjB;//ststic定义才会初始化 
FileStaticClass file_static_class;
int main( int argc,char * argv[])
{
    cout<<"main() started."<<endl;
    WrapperClassA objA;
//objA.singleton();   //只有去掉注释执行该语句时,innerObjA才被构造出来
    cout<<"main() terminated."<<endl;
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值