1. 条件语句中参数的顺序
左边更倾向于使用变化的,右边更倾向于使用稳定的。
1 if (length >= 10) 2 // or 3 if (10 <= length) 4 5 while (bytes_received < bytes_expected) 6 //or 7 while (bytes_expected > bytes_received)
1 //以前的惯例 2 if (obj = NULL) // 易产生bug 3 if (NULL == obj) // 可以预防bug 4 5 if (obj = NULL) // 现代编译器会给出警告 6 //test.cpp:185:11: 警告:建议在用作真值的赋值语句前后加上括号 [-Wparentheses] 7 // 185 | if (obj = NULL) 8 9 if ((obj = NULL)) // OK 10 if (obj == NULL) // OK
2. if / else 语句块的顺序
先处理正逻辑
1 if (!url.HasQueryParameter("expand_all")) { 2 response.Render(items); 3 } else { 4 for (int i = 0; i < items.size(); i++) { 5 items[i].Expand(); 6 } 7 } 8 9 //当说“不要去想粉红色的大象”时,不要就被“想粉红色的大象”淹没了 10 // 所以正向逻辑放在前面更易读 11 if (url.HasQueryParameter("expand_all")) { 12 for (int i = 0; i < items.size(); i++) { 13 items[i].Expand(); 14 } 15 } else { 16 response.Render(items); 17 }
负逻辑更简单/更危险/更有趣时,可以放在前面
1 if not file: 2 # Log the error ... 3 else: 4 # ...
3. ?:表达式(又名“三目运算符”)
富有争议的表达式
1 // 紧凑 2 time_str += (hour >= 12) ? "pm" : "am"; 3 4 // 冗长 5 if (hour >= 12) { 6 time_str += "pm"; 7 } else { 8 time_str += "am"; 9 }
然而这种表达式很有可能变得很难读
1 return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
要使理解的难度最小化,因此建议只在最简单的情况下使用?:(“三目运算符”)
4. 避免使用do/while循环
do/while奇怪之处在于:条件放在其“保护”的代码之后。读者需要读两遍代码,很不自然。
大多数do/while循环可以改成 while循环
1 //但是不要这样做 2 body 3 while (condition) { 4 body (again) 5 }
5. 从函数提前返回
”保护语句“在函数开头进行检查
6. 臭名昭著的goto语句
大部分时候需要避免使用goto语句。但是下面的情况除外:cleanup可以避免大段的重复代码。
1 if (p == NULL) goto cleanup; 2 //... 3 cleanup: 4 fclose(file1); 5 fclose(file2); 6 //... 7 return;
7. 最小化嵌套
嵌套很深的代码很难理解。看上去很简单的改动可能会使嵌套越来越深。
因此,当你对代码进行改动时,从全新的角度审视它,把它当成一个整体看待。
8. 通过提早返回来减少嵌套
9. 减少循环内嵌套,善用continue。
10. 你能理解执行的流程吗?
线程 / 信号量 / 中断处理程序 / 异常 / 函数指针和匿名函数 / 虚方法
上面这些幕后运行的代码很有用,甚至可以让代码冗余更少,更具可读性。
但是用的太多会令人难以理解,bug也更难跟踪。不要让代码中这些部分比例太高。