编程风格
第14条:宁要编译时和连接时错误,也不要运行时错误
摘要:能够在编译时做的事情,就不要推迟到运行时。
我记得在《重构》中有一条叫做“用测试取代异常”,我觉得差不多表达的是一个意思,即运行期错误的代价比较大,所以尽量转化成编译期错误和链接错误。这和人生的道理是相通的,我一直坚持认为,如果一定要摔跟头的话,越早摔越好。因为摔得越晚,成本越高。但同时还有一个问题就是,千万不要摔趴下起不来了。人生是很漫长的历程,要比拼的是韧劲。
第15条:积极使用const
摘要:const是我们的朋友,所以要设计的时候,const应该是默认选项,常量很安全,很省心。
使用const可以避免很多无意识的行为,当把一些成员从非const转变成const,也许你会吓一跳。
第16条:避免使用宏
摘要:宏是非常可怕的工具,他的攻击性一定超过了常人的想象。
宏的可怕之处在于它非常不可控,你不知道它能做些什么。如果你用过ACE就知道了,在ACE之下,main函数就不再是main函数了。Sorry,我无意模仿唐僧。但是能不用宏还是不要用宏吧。
宏也有好的用处,比如编译宏以及断言。
第17条:避免使用Magic Number
摘要:避免使用一些数字常量或者变量名,这常常带来混淆。
这是初学者喜欢的,我花了很大力气戒除。
第18条:尽可能局部地声明变量
摘要:避免作用域膨胀,对于变量来说,应该是生命域越短越好。
这样做明显的好处是可以避免名字空间的污染。另外说一点额外的话,函数中空间是在编译器就已经决定的。C++只有在函数的压栈和清栈过程中才涉及到栈的空间。这是C++非常强大的静态数据模型,他能保持非常稳定的运行。那么在同一个函数内,也存在不同的生命域,但这些变量的分配在编译时就已经决定,即使存在重名,即使生命域并不是整个函数范围,它都能独享一块空间,直到函数结束。
当然存在特例就是循环的相同计算外提。
第19条:总是初始化变量
摘要:一切从头开始,养成定义就初始化变量的习惯。
不想多说什么,我曾经写过一篇Blog——《错误发生原因之首》。看来万事都是殊途同归的。
第20条:避免函数过长,避免嵌套过深
摘要:短胜于长,平胜于优
这一问题在先前的《重构》中以及提及,因为过长和嵌套过多的函数是重构的重点关注对象,并且给出了各种解决办法。
第21条:避免跨编译单元的初始化依赖
摘要:保持初始化的顺序:不同编译单元中的名字空间级对象决不应该在初始化上互相依赖,因为谁都不知道初始化是什么样子的。
全局变量总是越少越好,而且全局变量之间的初始化不应该相互依赖。
第22条:尽量减少定义性依赖。避免循环依赖
摘要:不要过分依赖,不要互相依赖。
在我不是很熟悉C++的岁月里,我曾经被循环依赖深深的困扰,说起来是岁月,其实也就是不到一年前,其实有些东西可能看上去很困难,但实际上深入进去并非是那么复杂,所以不要被眼前的困难吓倒。
可能从设计的角度出发,这种依赖无法避免,而这也不一定全都是坏事,关键是要将这些依赖约束到相同的模块中,避免造成这种约束的扩散。
第23条:头文件应该自给自足
摘要:各司其职。
在头文件的编写中,需要考虑到这一头文件还需要依赖哪些资源,将这些需要引用的头文件包含在此头文件中,而不应该再由用户来确定,包含该头文件后还需要包含什么其他的头文件。可以在编译的时候,独立编译每一个头文件,确认是否可以通过编译。
第24条:总是编写内部#include保护符,决不要编写外部的#include保护符
摘要:为头文件添加保护
如果你使用的IDE是VC6,似乎这并不是一个问题,当你用VC没生成一个类的时候,在头文件中总有类似于
#if
!defined(
AFX_SS7ADAPTERTHREAD_H__3D06B248_9C32_41E4_9425_07AED625454F__INCLUDED_
)
#define
AFX_SS7ADAPTERTHREAD_H__3D06B248_9C32_41E4_9425_07AED625454F__INCLUDED_
……
#endif // !defined(AFX_SS7ADAPTERTHREAD_H__3D06B248_9C32_41E4_9425_07AED625454F__INCLUDED_)
引用这些不是为了增加空间,然而定义这么复杂的include guard,Microsoft一定有它的道理。这样的宏可以保证唯一性,而唯一性就是为了避免头文件的重复包含。include guard应该放在头文件内部,就像微软做的一样。很多人恨它,但它的存在一定有它的道理。