C++编程规范之编程风格(读书笔记)

第14条 宁要编译时和连接时错误,也不要运行时错误。

摘要
能够在编译时做的事情,就不要推迟到运行时:编写代码时,应该在编译期间使用编译器检查不变式(invariant),而不应该在运行时再进行检查。运行时检查取决于控制流和数据的具体情况,这意味着很难知道检查是否彻底。相比而言,编译时检查与控制流和数据无关,一般情况下能够获得更高的可信度。
讨论
C++语言中提供了强大的静态检查功能,而对自动运行时检查的支持则很少,使用静态检查功能可以带来下列好处:
     静态检查与数据和控制流无关。
     静态表示的模型更加可靠。
     静态检查不会带来运行时开销。

第15条 积极使用const
摘要
const是我们的朋友:不变的值更易于理解、跟踪和分析,所以应该尽可能地使用常量代替变量,定义值的时候,应该把const作为默认的选项:常量很安全,在编译时会对其进行检查(见第14条),而且它与C++的类型系统已浑然一体。不要强制转换const的类型,除非要调用常量不正确的函数(见第94条)。

第16条 避免使用宏
C++的宏的主要问题在于,它们表面上看起来很好,而实际上做的却是另一回事。宏会忽略作用域,忽略类型系统,忽略所有其他的语言特性和规则,而且会劫持它为文件其余部分所定义(#define)的符号。
例外情况
宏仍然是几个重要任务的惟一解决方案,比如#include保护符(guard)(见第24条),条件编译中的#ifdef和#if defined,以及assert的实现(见第68条)。
在条件编译(如与系统有关的部分)中,要避免在代码中到处杂乱地插入#ifdef。相反,应该对代码进行组织,利用宏来驱动一个公共接口的多个实现,然后始终使用该接口。
如果不想到处复制和粘贴代码段,那么可以使用宏(但要非常小心)。

第17条 避免使用“魔数”
摘要
程序设计并非魔术,所以不要故弄玄虚:要避免在代码中使用诸如42和3.14159这样的文字常量。它们本身没有提供任何说明,并且因为增加了难于检测的重复而使维护更加复杂。可以用符号名称和表达式替换它们,比如width * aspectRatio。

第18条 尽可能局部地声明变量
变量的生存期超过必需的长度时会产生以下几个缺点:
     它们会使程序更难以理解和维护。
     它们的名字会污染上下文。
     它们不能总是被合理地初始化。
解决方案很简单:尽可能局部地定义每个变量,通常就是在你有了足够的数据进行初始化的时候,而且恰恰就在首次使用变量之前。
例外情况
有时候将变量提出循环是有好处的(见第9条)。
因为常量并不添加状态,所以本条对常量不适用(见第17条)。

第19条 总是初始化变量
摘要
一切从白纸开始:未初始化的变量是C和C++程序中错误的常见来源。养成在使用内存之前先清除的习惯,可以避免这种错误,在定义变量的时候就将其初始化。
例外情况
硬件或者其他进程直接写入的输入缓冲区数据和volatile型数据不需要程序对其进行初始化。
 
第20条 避免函数过长,避免嵌套过深
摘要
短胜于长,平优于深:过长的函数和嵌套过深的代码块的出现,经常是因为没能赋予一个函数以一个紧凑的职责所致(见第5条),这两种情况通常都能够通过更好的重构予以解决。
讨论
请遵循这样的常识和常理:限制函数的长度和嵌套深度。以下所有的合理建议对这一点都有所裨益:
     尽量紧凑:对一个函数只赋予一种职责(见第5条)。
     不要自我重复:优先使用命名函数,而不要让相似的代码片断反复出现。
     优先使用&&:在可以使用&&条件判断的地方要避免使用连续嵌套的if。
     不要过分使用try:优先使用析构函数进行自动清除而避免使用try代码块(见第13条)。
     优先使用标准算法:算法比循环嵌套要少,通常也更好(见第84条)。
     不要根据类型标签(type tag)进行分支(switch)。优先使用多态函数(见第90条)。
例外情况
如果一个函数的功能无法合理地重构为多个独立的子任务(因为任何重构尝试都需要传递许多局部变量和上下文,使重构结果的可读性非但不好,反而更差),那么它的较长和嵌套较多就是合理的。但是如果有几个这样的函数都具有相似的参数,那么它们就有可能成为一个新类的成员。

第21条 避免跨编译单元的初始化依赖
摘要
保持(初始化)顺序:不同编译单元中的名字空间级对象决不应该在初始化上互相依赖,因为其初始化顺序是未定义的。这样做会惹出很多麻烦,轻则在项目中稍做修改就会引发奇怪的崩溃,重则出现严重的不可移植问题——即使是同一编译器的新版本也不行。

第22条 尽量减少定义性依赖。避免循环依赖
摘要
不要过分依赖:如果用前向声明(forward declaration)能够实现,那么就不要包含(#include)定义。
不要互相依赖:循环依赖是指两个模块直接或者间接地互相依赖。所谓模块就是一个紧凑的发布单元(见“名字空间与模块”部分的引言部分)。互相依赖的多个模块并不是真正的独立模块,而是紧紧胶着在一起的一个更大的模块,一个更大的发布单元。因此,循环依赖有碍于模块性,是大型项目的祸根。请避免循环依赖。

第23条 头文件应该自给自足
摘要
各司其责:应该确保所编写的每个头文件都能够独自进行编译,为此需要包含其内容所依赖的所有头文件。

第24条 总是编写内部#include保护符,决不要编写外部#include保护符
摘要
为头(文件)添加保护:在所有头文件中使用带有惟一名称的包含保护符(#include guard)防止无意的多次包含。
讨论
应该用内部包含保护符保护每个头文件,以避免在多次包含时重新定义。例如,头文件foo.h应该采用下面的一般形式:
#ifndef FOO_H_INCLUDED_
#define FOO_H_INCLUDED_
// ……文件内容……
#endif
定义包含保护符时,应该遵守如下规则:
     保护符使用惟一名称。
     不要自作聪明:不要在受保护部分的前后放置代码或者注释,要谨遵上面的标准形式。虽然如今的预处理器能够检测出包含保护符,但是它们的智商有限,只认识正好位于头文件开始和结束处的保护代码。
避免使用一些比较老的书中所提倡的已经过时了的外部包含保护符:
#ifndef FOO_H_INCLUDED_         // 不推荐
#include "foo.h"
#define FOO_H_INCLUDED_
#endif
例外情况
在一些非常罕见的情况下,可能需要多次包含一个头文件。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值