第三、四部分——变量、语句
“在声明变量时初始化”——减少未赋值的风险。
“尽可能减少变量的存活时间”——感觉如果按照推荐,一般子程序都写的很短,那么这个也就不重要了吧。
“一个好记的名字反应的通常是问题,而不是解决方案,是what而不是how”
“避免使用相似含义的名字,如果你能让2个变量交换名字还不妨碍理解的话,就说明都要重新改名了。”——越功能简单越名字容易相似,哪那么好改啊。
“避免使用数字,什么file1,file2”——有时候就是图省事,哈
“阅读代码的次数远远多于编码,所以命名变量应该是阅读方便而不是编写方便”——不过现在公司都是一人维护一个模块,基本很少有别人来读代码。
“编码使用神秘数值,要么枚举要么常量”
“有除法预防除0错误”——经常犯
“检查中间结果溢出”——这个很重要,考虑最后结果是不是会溢出,有时候中间变量的溢出更会导致问题。
“避免数量级相差巨大的数之间的加减”——浮点型很难保证精度。
枚举的好处:
1、可读性好。
2、返回值用枚举替代bool,可以返回更多的结果,比如有2种失败等。
——2种模式下用一个方法返回是哪种模式,千万别有bool而要用枚举,否则新增需求是非常麻烦的。
3、枚举对第一项和最后一项再给一个特殊的名字,便于循环
enum{
color_first=0,color_red=color_first,color_green,color_blue,……color_black,color_end=color_black,
};
——循环时的确方便
使用typedef来自定义数据类型,方便修改,同时方便移植。
——方便移植倒是很理解,不过方便修改这点倒是很少遇到。
把相关元素组织到结构体里面,然后对结构体操作。好处在于交换2组数据时非常方便。
用结构体传递参数给方法。好处是再增加一个参数时,不用那么麻烦的再改好几个地方了,而且函数声明定义也不会那么长一串。
把指针操作限制在子程序里面或者类里面操作。——比如链表里面的指针操作。
delete指针时销毁内存——这点挺好的,delete前bzero一把,就不会导致使用已经delete的数据,有时候崩溃有时候没问题的情况了。但是有个问题,如果是标准数据类型还好,或者结构体也还好,有长度,对于有string的类,怎么bzero啊,根本就没有具体长度。???
方法传递变量,建议用const引用来传递,减少一次复制。
把if-else里面正常处理和处理常见情况放在最前面处理。
default只用来处理输入错误。
注意空循环里有没有sleep,可能导致cpu占满。
循环条件要尽可能短,嵌套循环不要超过3层。把长寻访放到子程序里面。
goto在分配资源,使用资源后再释放资源的子程序里面比较有用,尤其是出错要退出的时候,大量的释放资源的代码很重复。
——用宏定义把所有的释放都变成一条语句也可以,不过似乎很难看的样子。
第十八章《表驱动法》
凡是能用逻辑语句来选择的事物,都可以通过查表法来选择。
使用逻辑虽然直白,但是逻辑太多,查表法反而更好。
——比如以前设计一个程序,查找某ip段属于某个域,用x[256][256]-list,查找速度和理解性上都比用数字段好多了。
把结果提前计算出来,放在表中直接查阅,比现计算要快。
或者把计算出来的结果存在表中,避免下次计算。
“最好是找一种好的方案同时避免引发灾难,而不要试图去寻找最佳答案。”
第五部分:代码改善
20软件质量概述:
外在特征:(给用户用)
正确性(错误的稀少)、可用性(用户学习和使用一个系统的容易程度)
效率(占用系统资源多少,执行速度)、可靠性(很长的雾故障时间)
完整性integrity【应该叫完善性】(如拒绝访问未授权数据、确保日期字段是有效数据等)
适应性(如果即可以在xp小跑,也能在w7下跑)、精确性(在正确性的基础之上判定完成工作的优劣程度)
健壮性(接受无效输入的容错处理,和压力承受能力。)
内在特征:(给其他程序员用)
可维护性(是否容易修改、容易增加功能、×××能、修正缺陷)
灵活性(用于其他用途时,修改的难度)【感觉没啥意义,其他用途还不如重写呢。写代码的时候就考虑灵活性,会导致限制很大】
可移植性(编译移植到其他平台)【与其为了可移植性导致效率下降,真不如重写一个算了。比如libevent为了夸平台,win上用的竟然是select。ACE在win上的完成端口还可以,但是在linux的epoll不是很好】
可重用性(某些功能用到其他程度里面的难易程度)
可读性(代码好读否)、可测试性
可理解性(可读性的加强版,更容易了解整体设计)
检查通读代码容易发现接口错误。
功能测试容易发现控制缺陷。
非正式测试一遍只能有50-60%的测试覆盖率。
编写无缺陷程序可能会让我们花费更少的时间。
21 协同构建:
相互协作的好处:开发人员都有盲点,但是其他人不一定有相同的盲点,所以让其他人检查自己的代码是有好处的。
结对编程:一个敲代码,一个在一边看。好处是人在有压力之下效率更高缩短时间表,能改善代码的质量和可读性,传播公司文化,执导培养新人等。【感觉好可怕,被一个人盯着去编码,难受】
正式检查、评审:
参加人员:
主持人(负责分配人员,分配任务,检查汇报结果,跟踪指派的结果。不能是作者)
作者(直接写文档、代码的人)
评审专家(在开会前找出缺陷,提交给作者和主持人)
记录员(评审专家一员,记录问题的确认,和新问题的记录)
步骤:
计划(作者提交主持人,主持人选定专家,开会时间)
概述(可选,加入专家不熟悉具体情况,作者做介绍)
准备(每个专家独立检查,有问题提交主持人和作者)
开会(主题是确认是不是问题,到确认就可以了,不应该去讨论解决方案。有新问题记录。不应该超过2个小时,很难保持精力,同时时间太长,专家在之前就不会认真找问题而在希望在会议中去找了。)
返工(确认记录,修复之)
跟进(主持人督查返工情况)
第三小时会议(有关人员讨论解决方案)
注意事项:
“很多公司发现,去掉或者合并某些步骤往往增加而不是降低了成本”
对事不对人,目的是找出缺陷,而不是探索替代方案,也不是批评作者水平低。
“90%的错误是在准备时发现的,10%是在会议上发现的。”
22开发者测试:
单元测试(单个子程序,单个类,或者单个小程序)
组件测试(个人理解是某个独立模块或子系统,涉及到多个程序员)
集成测试(多个子系统,多个开发团队)
回归测试(修改bug后的再次测试,防止这次修改导致其他错误)
系统测试(某些性能,安全,资源消耗等问题只能在这个层面测试)
测试是为了找出问题,测试永远不能证明程序中没有错误,测试本身并不能改善代码质量。
【建议先写测试用例再编码——本书提到很多次了,估计作者应该有切身体会。】
先写测试用例,会让你在开始写代码之前思考一下需求和设计,而这会催生出更高质量的代码。
使用容易手工检查的值作为测试输入。
“绝大多数错误都是在少数类中。”
“19%的错误都是对设计或需求的错误理解或误解导致的。”
测试数据本身出错的密度比测试代码还要高,可能导致程序员消耗几个小时的时间,发现原来是数据错了。
保留测试记录【公司一般都有bug记录和关系,感觉可以增加的就是“查找错误消耗的时间,修正错消耗的时间”】
23调试:
调试不是改进代码的方法,而是诊断代码缺陷的方法。
科学调试方法:
1、把错误状态稳定下来(找出必先的大致条件)
2、确定错误来源(收集错误相关数据,分析,构造一个假说,正式或者证伪这个假说)
3、修补缺陷
4、测试
5、查找还有类似错误否
如果没有重复性,就要考虑是不是初始化文件,或者是与时间有关,或者是野指针。
蛮力调试:对代码全部检查,从头开始设计和编码等等。
“对蛮力测试,你反应是做不了。但是往往花了2个小时去调试本来30分钟就能写完的代码。”
“动手之前要先了解问题。匆忙动手解决问题是你所能做的最低效的事情之一。”
“理解程序本身,而不仅仅是问题。”
“保存最初的源代码”【比如上传cvs,这点很重要,深有体会】
“修改代码一定要有适当的理由,而不是改动这里试试能不能解决问题。”
“一次只做一个改动。,改动越多越容易出错。”
“修改后检查自己的改动。”【修改后对比原先的代码很重要】
“把编译器设定为最严格,修改所有的报警的地方”【有必要吗,比如vc里面给一个枚举赋整型值也告警,这也都得修改吗?】
24重构
重构的理由:
具体见P565
比如:
代码重复(同样的一段代码,到处都是,每次一个修改得改好几个地方)
太多参数的子程序
……
超前设计(将来才会用到的功能)【感觉和前面的可维护性矛盾啊,不考虑超前,怎么适应未来的扩展功能?】
重构不等于重写。
----------------------------
【留个问题,上班的时候问问,g++在调试的时候能改内存的值吗?】
答:可以 gdb里面,输入“p 变量=新值”就可以了,另外类的符号重载在这里是无效的,比如string abc;那么不能p abc=“123” 而yoga用p abc.assign("123")
另外顺便发现了string的几个特点
char ch1[20] = {0};
ch1[0] = 'a';
ch1[10] = 'b';
string abc(ch1,20);
cout <<abc.length()<<";"<<abc.size()<<";"<<abc<<";"<<abc.c_str()<<";"<<endl;
打印结果应该是:20;20;ab;a;
转载于:https://blog.51cto.com/xzq2000/1767392