设为首页 加入收藏

TOP

Item 4: Make sure that objects are initialized before they’re used.(8)
2013-10-07 14:26:16 来源: 作者: 【 】 浏览:44
Tags:Item Make sure that objects are initialized before they used.

The reference-returning functions dictated by this scheme are always simple: define and initialize a local static object on line 1, return it on line 2. This simplicity makes them excellent candidates for inlining, especially if they’re called frequently (see Item 30). On the other hand, the fact that these functions contain static objects makes them problematic in multithreaded systems. Then again, any kind of non-const static object — local or non-local — is trouble waiting to happen in the presence of multiple threads. One way to deal with such trouble is to manually invoke all the reference-returning functions during the single- threaded startup portion of the program. This eliminates initialization- related race conditions.

Of course, the idea of using reference-returning functions to prevent initialization order problems is dependent on there being a reasonable initialization order for your objects in the first place.

If you have a system where object A must be initialized before object B, but A’s initialization is dependent on B’s having already been initialized, you are going to have problems, and frankly, you deserve them. If you steer clear of such pathological scenarios, however, the approach described here should serve you nicely, at least in single-threaded applications.

To avoid using objects before they’re initialized, then, you need to do only three things. First, manually initialize non-member objects of built-in types. Second, use member initialization lists to initialize all parts of an object. Finally, design around the initialization order uncertainty that afflicts non-local static objects defined in separate translation units.

Things to Remember

Manually initialize objects of built-in type, because C++(www.cppentry.com) only sometimes initializes them itself.

In a constructor, prefer use of the member initialization list to assignment inside the body of the constructor. List data members in the initialization list in the same order they’re declared in the class.

Avoid initialization order problems across translation units by replacing non-local static objects with local static objects.

non-local static(非局部静态)对象的构造次序问题,从理论上很容易理解,但实践中一旦碰到总会让程序员吐血。新手与明白其中精妙的C++(www.cppentry.com)程序员的唯一差距在于经验。只有积累一定的经验后,碰到这个问题引起的故障时才能快速定位,推断出问题的根源。但想完全规避这类问题,除非在编码规范中定下规矩,坚决不使用非局部静态对象。

在我个人的编程(www.cppentry.com)历史上,很少在同一问题上栽两次跟头,而这类问题则是个例外。

很多年前的一个项目中,我将一个类声明成了静态对象,而这个类引用了一个 static的vector 对象。结果这个vector的构造时序晚于静态对象。

在系统的运行过程中,这个静态对象的几个成员函数先于vector的构造函数被调用,导致在构造vector之前,首先调用了vector的resize方法。可怕的是,这个先于vector构造函数的调用虽然结果未定义,但在现实中却可以得到合法的结果。程序则可以正常地运行下去。当调用vector的构造函数后,程序的内部状态被破坏了。这个破坏却不是立刻暴露出来。等到程序挂掉,表现出bug的时候,已经很难追踪问题的根源了,甚至bug都难以重现。

追根溯源,除了编写C++(www.cppentry.com)代码过程中的不良习惯(使用non-local static对象)外,出现这个问题的原因还在于,C++(www.cppentry.com)语言对于未定义的数据上的执行行为也未定义,而这种未定义掩盖了问题,从表面上看,程序运行良好。比如,许多OS分配的内存空间内的数据都是以零填充的。而零对于C++(www.cppentry.com)对象来说经常就是默认值,这导致未被正确初始化的C++(www.cppentry.com)对象看起来也能暂时正常工作。

很难相信,我在碰到并检查出这个bug的十年后,居然又在一个新项目中制造了完全相同的bug。唯一的区别是,第二次我只用半小时就定位了问题代码。

从这个问题也能看出内建数组和STL中vector的不同点。虽然std::vector极力模拟出和数组同样的特性,但更复杂的实现却使得它与数组终归有所区别。若单从运行性能上看,内建数组倒是没有明显的优势。


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Item 4: Make sure that objects .. 下一篇用C++语言编写COM组件

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: