第 1 章 文件结构
1.4 头文件的作 用
早期的编程语言如 Basic 、 Fortran 没有头文件的概念, C++/C 语言的初学者虽然会用使用头文件,但常常不明其理。这里对头文件的作用略作解释:
( 1 )通过头文件来调用库功能。在很多场合,源 代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译 器会从库中提取相应的代码。
( 2 )头文件能加强类型安全检查。如果某个接口 被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
第 2 章 程序的版式
空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内 存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不空行。
2.6 修饰符的位置
若将 修饰符 * 靠近数据类型,例如:int* x; 从语义 上讲此写法比较直观,即x 是int 类型的 指针。
上述写法的弊端是容易引起误解,例如:int* x, y; 此处y 容 易被误解为指针变量。 虽然将 x 和 y 分行定义可以避免误解,但并不是人人都愿意这样做。
l 【规 则 2-6-1 】 应当将修饰符 * 和 & 紧靠变量名
例如:
char *name;
int *x, y; // 此处y 不会被误解为指针
第 3 章 命名规则
比较 著名的 命名规则当推 Microsoft 公司的“匈牙利”法,该 命名规则的主要思想是“ 在变量和函数名中加入前缀以增进人们对程序的理 解”。例如所有的字符变量均以ch 为前缀,若是指针变量则追加前缀p 。如果一个变量由ppch 开头,则表明它是指向字符指 针的指针。“匈牙利”法最大的缺点是烦琐。
【规则 3-1-3】 命 名规则尽量与所采用的操作系统或开发工具的风格保持一致。
【规则 3-1-6 】 变量的名字应当使用“名词”或者“形容词+名词”。
例如:
float value;
float oldValue;
float newValue;
【规则 3-1-7】 全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。类的成员函数应当只使用“动词”, 被省略掉的名词就是对象本身。
例如:
DrawBox(); // 全局函数
box->Draw(); // 类的成员函数
【规则 3-1-8】 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
例如:
int minValue;
int maxValue;
int SetValue(…);
int GetValue(…);
3.2 简单的 Windows 应用程序命名规则
例如 Windows 应用程序的标识符通常采用“大小写”混排的方式,如 AddChild 。而 Unix 应用程序的标识符通常采用“小写加 下划线”的方式,如 add_child 。别把这两类风格混在一起用。
3.2 简单的 Windows 应用程序命名规则
【规则 3-2-4】 静态变量加前缀 s_ (表示 static )。
例如:
void Init(…)
{
static int s_initValue; // 静 态变量
…
}
【规则 3-2-5】 如果不得已需要全局变量,则使全局变量加前缀 g_ (表示 global )。
例如:
int g_howManyPeople; // 全局变量
int g_howMuchMoney; // 全局变量
【规则 3-2-6】 类的数据成员加前缀 m_ (表示 member ), 这样可以避免 数据成员与成员函数的参数同名。
例如:
void Object::SetValue(int width, int height)
{
m_width = width;
m_height = height;
}
第 4 章 表达式和基本语句
4.3.2 整型变量与零值比较
l 【规则 4-3-2 】 应当将 整型变量用“== ” 或“!= ”直接与0 比较 。
假设 整型变量的名 字为value ,它与零值比较 的标准 if 语句如下:
if (value == 0)
if (value != 0)
不可模仿布尔变量的风格而写成
if (value) // 会让人误解 value 是布尔变量
if (!value)
4.3.3 浮点变量与零值比较
l 【规则 4-3-3 】 不可将 浮点变量用“== ”或“!= ”与任何数字比较 。
千万要留意,无论是float 还是double 类型的变量,都有精度限制。所以一定要避免将浮点变量用“== ” 或“!= ”与数字比较,应该设法转化成“>= ” 或“<= ”形式。
假设 浮点变量的名字为x ,const float EPSINON = 0.00001; 应当将
if (x == 0.0) // 隐含 错误的比较
转化为
if ((x>=-EPSINON) && (x<=EPSINON))
其中EPSINON 是允许的误差(即精度)。
4.3.5 对if 语句的补充说明
有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程序员为了防止将 if (p == NULL) 误写成 if (p = NULL) , 而有意把p 和NULL 颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p) 是错误的,因为NULL 不能被赋值。
程序中有时会遇到if/else/return 的组合,应该将如下不良风格的程序
if (condition)
return x;
return y;
改写为
if (condition)
{
return x;
}
else
{
return y;
}
或者改写成更加简练的
return (condition ? x : y);
4.4 循环语句的效率
C++/C循环语句中,for 语句使用频率最高,while 语句其次,do 语句很少用。本节重点论述循 环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。
l 【建议 4-4-2 】 如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。示例4-4(c) 的 程序比示例4-4(d) 多执行了N-1 次逻 辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N 非常大,最好采用示例4-4(d) 的写法,可以提高效 率。如果N 非常小,两者效率差别并不明显,采用示例4-4(c) 的 写法比较好,因为程序更加简洁。
for (i=0; i<N; i++) { if (condition) DoSomething(); else DoOtherthing(); } | if (condition) { for (i=0; i<N; i++) DoSomething(); } else { for (i=0; i<N; i++) DoOtherthing(); } |
表4-4(c) 效率低但程序简洁 表4-4(d) 效率高但程序不简洁
l 【建议 4-4-1 】 在多重循环中,如果有可能,应当将最长的 循环放在最内层,最短的循环放在最外层,以减少CPU 跨切循环层的次数。例如示例4-4(b) 的效率比示例4-4(a) 的高。
for (row=0; row<100; row++) { for ( col=0; col<5; col++ ) { sum = sum + a[row][col]; } } | for (col=0; col<5; col++ ) { for (row=0; row<100; row++) { sum = sum + a[row][col]; } } |
示例4-4(a) 低效率:长循环在最外层 示 例4-4(b) 高效率:长循环在最内层
4.7 goto 语句
自从提倡结构化设计以来,goto 就成了有争 议的语句。首先,由于goto 语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格。其 次,goto 语句经常带来错误或隐患。它可能跳过了某些对象的构造、变量的初始化、重要的计算等语 句,例如:
goto state;
String s1, s2; // 被goto 跳
int sum = 0; // 被goto 跳 过
…
state:
…
如果编译器不能发觉此类错误,每用一次goto 语句都可能留下隐 患。
很多人建议废除C++/C 的goto 语句,以绝后患。但实事求是地说,错误是程序员自己造成的,不是goto 的 过错。goto 语句至少有一处可显神通,它能从多重循环体中咻地一下子跳到外面,用不着写很多次 的break 语句; 例如
{ …
{ …
{ …
goto error;
}
}
}
error:
…
就象楼房着火了,来不及从 楼梯一级一级往下走,可从窗口跳出火坑。所以我们主张少用、慎用goto 语句,而不是禁用。