目录:
1、文件结构
1.1、版权和版本的声明
1.2、头文件的结构
1.3、定义文件的结构
1.4、头文件的作用
1.5、目录结构
2、程序的版式
2.1、空行
2.2、代码行
2.3、代码行内的空格
2.4、对齐
2.5、长行拆分
2.6、修饰符的位置
2.7、注释
3、命名规则
3.1、共性规则
3.2、Windows 应用程序命名规则
4、表达式和基本语句
4.1、运算符的优先级
4.2、复合表达式
4.3、if 语句
4.3.1、布尔变量与零值比较
4.3.2、整型变量与零值比较
4.3.3、浮点变量与零值比较
4.3.4、指针变量与零值比较
5、常量
5.1、为什么需要常量
5.2、const 与 #define 的比较
5.3、常量定义规则
1、文件结构
每个 C++/C 程序通常分为两个文件。一个文件用于保存程序的声明(declaration),称为头文件。另一个文件用于保存程序的实现( implementation),称为定义( definition)文件。
1.1、版权和版本的声明
版权和版本的声明位于头文件和定义文件的开头,主要包括:
- 版权信息;
- 文件名称,标识符,摘要;
- 当前版本号,作者/修改者,完成日期;
- 版本历史信息。
示例:
1.2、头文件的结构
头文件由三部分内容组成(参见1.1):
- 头文件开头处的版权和版本声明;
- 预处理块;
- 函数和类结构声明等。
1.为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。
2.用#include <filename.h>
格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。
3.用#include “filename.h”
格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。
4.头文件中只存放“声明”而不存放“定义”。
在 C++ 语法中,类的成员函数可以在声明的同时被定义,并且自动成为内联函数。这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。建议将成员函数的定义与声明分开,不论该函数体有多么小。
5.不提倡使用全局变量,尽量不要在头文件中出现象extern int value 这类声明。
示例:
1.3、定义文件的结构
定义文件有三部分内容:
- 定义文件开头处的版权和版本声明;
- 对一些头文件的引用;
- 程序的实现体(包括数据和代码)。
示例:
1.4、头文件的作用
通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。
头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
1.5、目录结构
如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保存于不同的目录,以便于维护。
例如,可将头文件保存于 include 目录,将定义文件保存于source 目录(可以是多级目录)。
如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”。为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。
2、程序的版式
版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序风格的重要构成因素。
2.1、空行
空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。
1.在每个类声明之后、每个函数定义结束之后都要加空行。
2.在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。
示例:
2.2、代码行
1.一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。
2.if、for、while、do 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误。
3.尽可能在定义变量的同时初始化该变量(就近原则)。
示例:
2.3、代码行内的空格
1. 关键字之后要留空格。
2.函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。
3.(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。
4.‘,’之后要留空格,如Function(x, y, z)。如果‘;’不是一行的结束符号,其后要留空格,如for (initialization; condition; update)。
5.赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“ ||”、“<<”,“^”等二元操作符的前后应当加空格。
6.一元操作符如“!”、“~”、“++”、“–”、“&”(地址运算符)等前后不加空格。
7.对于表达式比较长的for 语句和if 语句,为了紧凑起见可以适当地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d))。
示例:
2.4、对齐
1.程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。
2.{ }之内的代码块在‘{’右边数格处左对齐。
示例:
2.5、长行拆分
1.代码行最大长度宜控制在70 至80 个字符以内。
2.长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
示例:
2.6、修饰符的位置
应当将修饰符 * 和& 紧靠变量名。
char *name;
int *x, y; // 此处y 不会被误解为指针
2.7、注释
注释通常用于:
- 版本、版权声明;
- 函数接口说明;
- 重要的代码行或段落提示。
1.注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主,注释太多了会让人眼花缭乱。注释的花样要少。
2.边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
3.注释应当准确、易懂,防止注释有二义性。
4.注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。
5.当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。
示例:
3、命名规则
3.1、共性规则
1.标识符应当直观且可以拼读,可望文知意,不必进行“解码”。
2.标识符的长度应当符合“min-length && max-information”原则。
3.命名规则尽量与所采用的操作系统或开发工具的风格保持一致。例如 Windows 应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix 应用程序的标识符通常采用“小写加下划线”的方式,如add_child。
4.变量的名字应当使用“名词”或者“形容词+名词”。
5.全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。
6.用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
int minValue;
int maxValue;
3.2、Windows 应用程序命名规则
1.类名和函数名用大写字母开头的单词组合而成。
2.变量和参数用小写字母开头的单词组合而成。
3.常量全用大写的字母,用下划线分割单词。
4.静态变量加前缀s_(表示static)。
5.全局变量加前缀g_(表示global)。
6.类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。
7.为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反映软件性质的前缀。
4、表达式和基本语句
4.1、运算符的优先级
示例:
4.2、复合表达式
1.不要编写太复杂的复合表达式。
2.不要有多用途的复合表达式。
3.不要把程序中的复合表达式与“真正的数学表达式”混淆。
4.3、if 语句
4.3.1、布尔变量与零值比较
不可将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。
根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE 的值究竟是什么并没有统一的标准。例如Visual C++ 将 TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。
假设布尔变量名字为flag,它与零值比较的标准if 语句如下:
if (flag) // 表示flag 为真
if (!flag) // 表示flag 为假
其它的用法都属于不良风格,例如:
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
4.3.2、整型变量与零值比较
应当将整型变量用“==”或“!=”直接与0 比较。
假设整型变量的名字为value,它与零值比较的标准if 语句如下:
if (value == 0)
if (value != 0)
4.3.3、浮点变量与零值比较
不可将浮点变量用“==”或“!=”与任何数字比较。
无论是float 还是double 类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
假设浮点变量的名字为x,应当将
if (x == 0.0) // 隐含错误的比较
转化为
if ((x>=-EPSINON) && (x<=EPSINON))
其中EPSINON 是允许的误差(即精度)。
4.3.4、指针变量与零值比较
应当将指针变量用“==”或“!=”与NULL 比较。
指针变量的零值是“空”(记为NULL)。尽管NULL 的值与0 相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if 语句如下:
if (p == NULL) // p 与NULL 显式比较,强调p 是指针变量
if (p != NULL)
5、常量
常量是一种标识符,它的值在运行期间恒定不变。C 语言用 #define 来定义常量(称为宏常量)。C++ 语言除了 #define 外还可以用const 来定义常量(称为const 常量)。
5.1、为什么需要常量
如果不使用常量,则:
- 程序的可读性(可理解性)变差。程序员自己会忘记那些数字或字符串是什么意思,用户则更加不知它们从何处来、表示什么。
- 在程序的很多地方输入同样的数字或字符串,难保不发生书写错误。
- 如果要修改数字或字符串,则会在很多地方改动,既麻烦又容易出错。
尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串。
5.2、const 与 #define 的比较
C++ 语言可以用const 来定义常量,也可以用 #define 来定义常量。但是前者比后者有更多的优点:
- const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
- 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
在C++ 程序中只使用const 常量而不使用宏常量,即const 常量完全取代宏常量。
5.3、常量定义规则
1.需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
2.如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。
const float RADIUS = 100;
const float DIAMETER = RADIUS * 2;
FROM《高质量C++编程指南》
《高质量C++编程指南》