【高质量C/C++】—— 2. 程序的版式
版式不会影响程序的功能,但是会影响程序的阅读性,阅读性不好的程序维护的成本是非常高的,所以一定要有良好的代码风格
一、代码的版式
1. 空行
空行有分隔程序的作用,会让程序布局更清晰。空行会占用一些源文件大小,但不影响编译后的可执行文件大小。
规则:
- 每个 类声明后 和 函数定义 结束后都要加空行
- 在一个函数体,逻辑上密切相关的语句之间不加空行,其它地方应加空行分隔
// 函数之间的空行
void function1(... ...)
{
... ...
}
// 空行
void function2(... ...)
{
... ...
}
// 空行
void function3(... ...)
{
... ...
}
// 函数内部的空行
while (condition)
{
statement1;
// 空行
if (condition)
{
statement2;
}
else
{
statement3;
}
// 空行
statement4;
}
2. 代码行
规则:
- 一行代码只做一件事。如只定义一个变量,只定义一个语句。这样的代码更容易阅读,更方便写注释。
if
、for
、while
、do
等语句独占一行,执行语句不要紧跟其后。不论多少语句执行都要加{}
// 良好的代码风格
int width; // 宽度
int height; // 高度
int depth; // 深度
x = a + b;
y = c + d;
z = e + f;
if (width < height)
{
dosomething();
}
for (int i=0; i<n; i++)
{
printf("%d ", i);
}
other();
// 风格不良的代码行
int width, height, depth;
x = a + b; y = c + d; z = e + f;
if (width < height) dosomething();
for (int i=0; i<n; i++)
printf("%d ", i);
other();
建议:
- 尽可能在定义变量的时候同时初始化该变量,若是变量的引用处和定义相隔较远,就会忘记并使用未初始化的变量。
- 变量在初始化时若是没有马上要使用的值,则可以暂时赋值为
0
或NULL
(C++中为nullptr
),这个建议非常有用,所以一般情况下请把它当作一个好的规则使用
int width = 10; // 宽度
int height = 10; // 高度
int depth = 10; // 深度
int num = 0;
int* p = nullptr;
3. 代码行内的空格
规则:
- 标识符(函数名和关键字):
- 关键字后要留空格,对于
const
、inline
、case
等关键字不留空格会使编译器无法辩析关键字,导致编译失败,这种情况众人皆知。重点是if
、for
、while
等关键字之后要留一个空格再跟左括号,以突出关键字。 - 函数名后面不要留空格,紧跟左括号,与关键字区别
- 关键字后要留空格,对于
if (year >= 2000) // 良好的风格
;
if(year >= 2000) // 不良的风格
;
void Function1(int x, int y, int z); // 良好的风格
void Function2 (int x, int y, int z); // 不良的风格
- 运算符:
(
向后紧跟,)
、,
、;
向前紧跟,紧跟处不留空格,
或;
如果不是一行的结束,则要留出空格- 赋值、比较、算术、位域、逻辑等二元操作符的两侧应加空格。
!
、~
、++
、--
、&(取地址)
等一元操作符前后不加空格- 像
[]
、.
、->
这类操作符前后不加空格
if (year >= 2000) // 良好的风格
;
if (year>=2000) // 不良的风格
;
if ( year >= 2000) // 不良的风格
;
int i = 0;
i++; // 良好的风格
i --; // 不良的风格
&i; // 良好的风格
~ i; // 不良的风格
arr[5] = 0; // 良好的风格
arr[ 5 ] = 0; // 不良的风格
a.Function(); // 良好的风格
a . Function(); // 不良的风格
b->Function(); // 良好的风格
b -> Function(); // 不良的风格
for (int i = 0; i < n; i++) // 良好的风格
;
for (int i=0; i<n; i++) // 良好的风格,将逻辑紧密的语句写的紧凑也是一个不错的风格,推荐在表达式略复杂时使用
;
for (int i=0;i<n;i++) // 不良的风格
;
建议:
- 表达式:
- 对于表达式比较长的for、if语句,为了紧凑起见可以适当去掉一些空格。
- 在长表达式中,建议使用括号将逻辑紧密的表达式括在一起,这样既保证运算符优先级,有保证层次清晰
int c = a < b ? a * b : a - b;
int d = a<b ? a*b : a-b;
if ((a<=b) && (c<=d)) // 良好的风格
;
if (a <= b && c <= d) // 不良的风格
;
4. 对齐
规则:
- 程序的分界符
{
和}
独占一行且位于同一列,同时与引用它们的语句左对齐 {}
之内的代码应在{
的下一行右边一个tab
键的距离对齐
// 良好的风格
void Function1(int x)
{
statement;
}
// 不良的风格
void Function2(int y) {
statement;
}
// 良好的风格
if (condition)
{
statement1;
}
else
{
statement2;
}
// 不良的风格
if (condition) {
statement1;
} else {
statement2;
}
// 良好的风格
for (int i=0; i<n; i++)
{
for (int j=0; j<n; j++)
{
statement;
}
}
5. 长行拆分
规则:
- 代码的最大长度控制在80个字符以内。代码过长会使眼睛看不过来,也不便打印
- 长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(突出操作符)。拆分的新行要适当缩进,排版整齐。
// 良好的风格
if ((very_long_variable1 >= very_long_variable2)
&& (very_long_variable3 <= very_long_variable4)
&& (very_long_variable5 == very_long_variable6))
{
dosomething();
}
// 良好的风格
virtual CMatrix CMultiplyMatrix(CMatrix leftMatrix,
CMatrix rightMatrix);
// 良好的风格
for (very_long_initialization;
very_long_condition;
very_long_update)
{
dosomething();
}
6. 修饰符位置
修饰符*
和&
应该靠近数据类型还是靠近变量名,这是个有争议的话题。
修饰符靠近数据类型,如int* x
从语义上看此写法比较直观,及x使int
类型的指针。但是当此写法声明多个变量时容易引起误解,如int* x, y
,使y容易被当作int*
类型的指针。
规则:
- 修饰符
*
和&
应当紧靠变量名
char *name;
char *x, *y;
二、注释
C语言的注释符为/*注释内容*/
,在C++中,程序块的注释通常采用/*注释内容*/
,行注释一般采用//注释内容
。
注释通常用于:
- 版本、版权声明
- 函数接口说明
- 重要代码行或段落提示
规则:
- 注释的花样要少。注释是对代码的提示,而不是文档,不可喧宾夺主,注释太多会让人眼花缭乱。如果代码本来就是清楚的,则不必要加注释,否则多此一举
- 边写代码边加注释,修改代码同时修改注释,保证注释与代码的一致性
- 注释应当准确、易懂,防止有二义性注释。错误的注释比不写注释更有害
- 尽量避免在注释中使用缩写,特别是不常用的缩写
- 注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不要放在下方
- 当代码比较长时,特别是多重嵌套,应当在一些段落的结束处加注释,便于阅读
/*
* 函数介绍:
* 输入参数:
* 输出参数:
* 返回值 :
*/
void Function(float x, float y, float z)
{
if (... ...)
{
... ...
while (... ...)
{
... ...
} // end of while
... ...
} //end of if
} //end of Function
三、类的版式
类的版式主要有2种:
- 将
private
类型的数据写在前面,而将public
类型的函数写在后面,这种版式的程序员主张类的设计“以数据为中心”,重点关注类的内部结构- 将
public
类型的函数写在前面,而将private
类型的数据写在后面,这种版式的程序员主张类的设计“以行为为中心”,重点关注类应该提供什么样的接口
建议:
- 建议采用“以行为为中心”的书写方式。首先考虑类应该提供什么样的接口,这样不仅让自己在设计类的时候思路清晰,而且方便别人阅读,用户最关心的是接口,而不是一堆私有数据成员。
//以行为为中心的版式
class A
{
public:
void Function1(void);
void Function2(void);
... ...
private:
int a;
int b;
... ...
}
//以数据为中心的版式
class A
{
private:
int a;
int b;
... ...
public:
void Function1(void);
void Function2(void);
... ...
}