代码复审Check List
概要部分
-
代码能符合需求和规格说明么?
符合。针对-c和-s可以将正确的结果输出到相应的sudoku.txt,并在规定的时间内求解。
-
代码设计是否有周全的考虑?
有的。我分别测试了如下几个示例:
sudoku.exe sudoku.exe -c -1 sudoku.exe -c 1000000000 sudoku.exe -s abc (其中abc文件不存在) sudoku.exe -s sudoku.exe -c -s sudoku.exe -c 100 abcdefg
发现这些情况都会输出"invalid parameters",并且对-s 一个不存在的文件会输出"file not exist"
-
代码可读性如何?
- 变量名,函数名的命名比较规范,符合“匈牙利命名法”,能让读者一眼看出是什么意思。例如
matrixLen
,rowChange()
,initPermutation()
等等。 - 变量的使用符合就近原则,基本上都用的函数的局部变量而没有用全局变量,生存周期合理。
- 封装成了一个类,不同的功能抽象成不同的函数,每个函数不会承载很多功能。
- 关键部分有注释。
因此总体的可读性还是不错的。
- 变量名,函数名的命名比较规范,符合“匈牙利命名法”,能让读者一眼看出是什么意思。例如
-
代码容易维护么?
一般。建议将struct换成类,并对以一些函数写成接口。
-
代码的每一行都执行并检查过了吗?
单步执行并检查过了。
设计规范部分
-
设计是否遵从已知的设计模式或项目中常用的模式?
没有采用什么设计模式
-
有没有硬编码或字符串/数字等存在?
有。比如下段:
int per[3]; for (int i = 0; i < 3; i++) per[i] = row + i; do { int temp[3][matrixLen]; for (int i = row; i < row + 3; i++) { memcpy(temp[i % 3], newSeed[per[i % 3]], sizeof(temp[0])); } for (int i = row; i < row + 3; i++) { int o = i % 3; ....
这里的3是硬编码,可以说是一个让人困惑的“神仙数”了。
-
代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)?
没有。我的代码是在OSX平台上的Xcode写的,因此完全没有依赖到Windows的相关东西。
-
开发者新写的代码能否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现?
重写了判断两个字符数组是否相等和将字符串转化为数字的方法。
bool stringEqual(char *a, char *b);
int stringToNum(char *s);
-
有没有无用的代码可以清除?(很多人想保留尽可能多的代码,因为以后可能会用上,这样导致程序文件中有很多注释掉的代码,这些代码都可以删除,因为源代码控制已经保存了原来的老代码。)
有。除了在solver.h里的中间有100多行的注释外,还有一些注释掉的输出的无关语句。例如:
//freopen("2.out", "w", stdout);
//printf("time cost:%.3f", (endTime - beginTime) / CLOCKS_PER_SEC);
代码规范部分
- 修改的部分符合代码标准和风格么(详细条文略)?
代码书写基本符合《构建之法》4.2所讲的代码风格规范,但是风格却不怎么统一。if后的{ 放在同一行还是下一行就是让人非常困惑。
例如下图:
无关空行就没有必要了,中文注释也是。
具体代码部分
-
有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常?
除了void与bool函数,int函数都是非常简单的函数,并作了检查。
-
参数传递有无错误,字符串的长度是字节的长度还是字符(可能是单/双字节)的长度,是以0开始计数还是以1开始计数?
没有错误。是字节的长度。从0开始。
-
边界条件是如何处理的?Switch语句的Default是如何处理的?循环有没有可能出现死循环?
矩阵边界是0到8,数独边界是0到80.不会出现死循环。
-
有没有使用断言(Assert)来保证我们认为不变的条件真的满足?
单元测试里有断言,具体的代码没有。
-
对资源的利用,是在哪里申请,在哪里释放的?有没有可能导致资源泄露(内存、文件、各种GUI资源、数据库访问的连接,等等)?有没有可能优化?
类对象或者函数里的在占空间申请,结束调用或函数结束的时候就释放了。
全局变量在对空间申请,程序结束释放。没有用到指针动态分配空间,文件及时关闭了,因此不会内存泄露。
可以采用牺牲空间换时间的方式提升IO性能。无需每次求出数独就立即输出,我们可以先保存到字符数组进行统一输出。多线程求解数独肯定会时间更快。
-
数据结构中是否有无用的元素?
经检查没有
效能
-
代码的效能(Performance)如何?最坏的情况是怎样的?
第一部分生成数独比较好,第二部分求解数独一般。
在助教的机器上,生成100w个最终数独要1.222s,求解100w个数独要94.55s
-
代码中,特别是循环中是否有明显可优化的部分(C++中反复创建类,C#中string的操作是否能用StringBuilder 来优化)?
对于下面这个执行次数非常多的函数,我认为可以通过内联函数预编译或者宏定义来提升性能。
int encode(int a, int b, int c) {
return (a * matrixLen + b) * matrixLen + c + 1;
}
-
对于系统和网络调用是否会超时?如何处理?
没有相关调用。
可读性
-
代码可读性如何?有没有足够的注释?
可读性具体见第一大点的第3小点。关键部分注释挺多,一些细节的地方可以补充简单写一点。
可测试性
-
代码是否需要更新或创建新的单元测试?
有些格式可以修改一下。单元测试不需要创建新的,目前足够。
-
还可以有针对特定领域开发(如数据库、网页、多线程等)的核查表。
与本项目无关。
最后附上相应部分comment链接:
https://github.com/yaoling1997/softwareFirstHomework/commit/cf5a6564f901f590500438cde91d625e9da6858f
设计一个代码规范
我使用的代码规范工具是cpplint3.0
最初始的时有246个错误。主要有:
- 发现了tab。这个占据了绝大多数。换成四个空格。
- 没有copyright。
- 发现c的头文件出现在c++的后面。
- 一行的字符超过了80.
- 赋值号的两端少了空格。
- 注释和代码至少差两个空格。
- //和注释的内容要有一个空格。
- 行末不许出现多余空格。
- { 不能单独出现在一行,要跟着判断条件空的一个空格。
- 不许在判断后出现空行,所谓的Redundant blank line.
- 不要把多条语句放在一行。比如
if (a>b) return ;
要放在两行写。 - else需要出现在之前的if的}的同一行。
return;
return和;中不许有空格。- 不许用namespace,于是我改成了
using std::cin;
等等
经过一番修改之后,总算从246个errors到了done! 见下图,编译运行也没有问题。不容易!撒花!
-
工具提供的代码规范和你个人的代码风格有什么不同?
见上,很多不同。
-
工具提供的代码规范里有哪些部分是你之前没有想到的?
不允许有TAB!!!据说是不同环境的长度不同,还有很多的编码规范强制不允许用tab。
然后不允许用using namespace std;
我在Stack Overflow看到了这个帖子。这里面说了,using namespace std 会将各种名称导入全局命名空间,导致的后果就是可能有各种模糊的地方。例如std::count(之前都不知道有这样的东西),总之将会带来很多不必要的冲突。 -
为什么要这样规范?这样的规范有意义吗?
有意义。提高了代码的可读性、扩展性、在多人协同编程时效果显著。
-
最后,请根据构建之法书上编码规范里提到的那些要点整理一份你们在结对编程时使用的代码规范。
1, 用4个空格代替tab。
2, 开头需注明copyright。
3, c的头文件要申明在c++的前面。
4, 一行的字符不超过80。
5, 赋值号的两端需要有空格。
6, 注释和代码至少差两个空格。
7, //和注释的内容之间要有一个空格。
8, 行末不许出现多余空格。
9, { 不能单独出现在一行,要跟着判断条件空的一个空格。
10, 不许在判断后出现空行,所谓的Redundant blank line。
11, 不要把多条语句放在一行。比如if (a>b) return ;
要放在两行写。
13, else需要出现在之前的if的}的同一行。
14,return;
return和;中不许有空格。
15, 不许用namespace,可以替换为using std::cin;
等等。
16, 类名/结构体名首字母大写。
17, 变量名第一个单词首字母小写,其余单词首字母大写
18, 一行只能定义一个变量
19, 不能使用goto语句
最后附上partner滑稽照orz: