代码可读性
命名规范
函数命名规则(驼峰命名法)
普通函数的函数名由若干个单词组成,第一个单词全部小写,第二个单词开始首字母大写。
1 | bool getMeanValue(...); |
a. 如果是 inline 类型的函数,在函数名前加下划线 _
1 | inline int _getCuberInterpolationValue(…); |
b. 如果是 static 类型的函数,函数名第一个单词首字母大写
1 | static int OpenFiles(…); |
变量命名规则(匈牙利命名法)
- 原则 1:禁止使用简单英文单词命名变量,如
min
,max
,left
,right
,会造成和某些库保留变量名的冲突 - 原则 2: 变量作用域(w/m/无) + 指针/数组(p/pp/ppp/无) + 变量类型(c/u/n/f/d/s/v 等) + 变量名称
- 原则 3:迭代变量允许但不推荐
i
,j
,k
,m
,n
。但尽量使用有意义的迭代变量名称,如:_nFilesIdx
,_nUser_i
、nMember_i
表 1 变量作用域命名规则
变量作用域 | 前缀 | 例子 | 意义 |
---|---|---|---|
全局变量 | w | wnValue | 全局 int 型变量 |
静态变量 | S (大写) | SnValue | 静态 int 型变量 |
类变量 | m | mnValue | 类 int 型变量 |
普通变量 | 不需要 | nValue | int 型变量 |
临时变量 | _ | _nValue | 临时 int 型变量 |
表 2 指针/数组命名规则
指针/数组 | 前缀 | 例子 | 意义 |
---|---|---|---|
一维 | p | pdValues | 普通一维 double 型数组 |
二维 | pp | ppdValues | 普通二维 double 型数组 |
三维 | ppp | pppdValues | 普通三维 double 型数组 |
非指针 | 无 | dValue | 普通 double 型对象 |
表 3 变量类型命名规则
变量类型 | 前缀 | 例子 | 意义 |
---|---|---|---|
char | c | cValue | |
unsigned char, byte | u | uValue | |
short, unsigned short | n | nValue | |
int, unsigned int | |||
long, long long | l | lValue | |
float | f | fVafue | |
double | d | dVadue | |
bool | b | bVabue | |
char*, string | s / str / sz | strValue | |
list, vector | v | vValues | 链表/容器 |
file | in/out/fi | inFile / outFile / fiOutput | 输入输出文件 |
map, hash | map | mapKeyValues | 映射表/哈希表 |
整洁、对齐
- 错误范例
1 | int main(int argc, char* argv[]) |
- 修改
运算符与变量之间空格;;
和,
等表示间隔的符号后面空格;语句之间换行;代码片段之间空格;注释中,中英文之间尽量空格;注释对齐;缩进对齐。
1 | int main(int argc, char* argv[]) |
编写注释
- 精简:没有太大用处的不写,能从代码直接看出含义的不写,类中的
get
、set
方法不写;
1 | // The first String is student's name |
1 | // Student' name -> Student's score |
- 不因为写了注释就给变量随意取名;
- 通过注释来记录当前解决方案的算法过程,使得读者易于理解;
- 注释用于提醒一些特殊情况;
标记 | 用法 |
---|---|
TODO | 待做 |
FIXME | 待修复 |
HACH | 粗糙的解决方案 |
XXX | 危险!这里有重要问题 |
1 | //TODO: 设置源和目的地块适宜性(需要外部读入,是否自己输入这部分还要修正) |
- 添加测试用例来说明;
1 | //... |
- 在很复杂的函数调用中对每个参数标上名字
1 | int a = 1; |
- 对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面;
- 函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等;
1 | /************************************************* |
- 说明性文件(如头文件
.h
文件、.inc
文件、.def
文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明;
1 | /************************************************************ |
- 注释量一般不低于
20%
。(要求不低于30%
)
拆分长表达式
拆分前
1 | if (split(line, ',')[0].strip() == "SYSU" && split(line, ':')[1].strip() == "GIS") |
拆分后
1 | sList = split(line, ':'); |
减小变量的作用域
- 尽量不使用全局变量
- 作用域越小,越容易定位到变量所有使用的代码区间,这在动态类型的语言(例如Python,Javascript)中尤为重要。
任务分解,函数抽取
- 大问题拆分成小问题。首先应该明确一个函数的高层次目标,然后对于不是直接为了这个目标工作的代码,抽取出来放到独立的函数中。
- 并非函数抽取的越多越好,如果抽取过多,在阅读代码的时候可能需要不断跳来跳去。
- 函数抽取也用于减小代码的冗余。
1 | // 问题: |
No过度设计
编码过程会有很多变化,过度设计的内容到最后往往是无用的。确保代码的rubust,模块分解需要适度,太零碎的模块不易于整合。
例如以上计算两个向量之间最小的余弦距离,没必要再另外写一个查找最小值的函数。
Coding之前整理逻辑,书写伪代码
先用自然语言书写代码逻辑,也就是伪代码,然后再写代码,这样代码逻辑会更清晰。
Algorithm Bagging.
Input:
- D: a set of d training tuples
- k: the number of models in the ensemble
- M: a classification learning scheme(decision tree algorithm, naive Bayesian, etc.)
Output: the ensemble—a composite model M
Method:
- for i = 1 to k do //create k models
- create bootstrap sample D_i, by sampling D with replacement
- use D_i and the learning scheme to derive a model M_i
- endfor
指针内存管理
一维指针
为防止内存泄漏造成程序不稳定,在类和函数中声明的指针一定要先置 NULL
或 nullptr
;每次使用前先判断是否为 NULL
,若需要更新先 delete
再 new
;并且在类的析构函数中或函数尾部对该指针 delete
并置 NULL
或 nullptr
处理。
1 | // 先判断是否为空 |
二维指针
1 | int nRows = 10, nCols = 20; |