首页
社区
课程
招聘
[翻译]关于C++编程的42条建议(八)
发表于: 2017-3-10 19:35 3839

[翻译]关于C++编程的42条建议(八)

2017-3-10 19:35
3839




下面的代码来自Appleseed项目。代码中包含的错误被PVS-Studio诊断为:V719 switch语句没有覆盖枚举InputFormat”的所有值,少了InputFormatEntity.



解释


有的时候我们需要在已经存在的枚举里加入新的元素,当我们做这个操作的时候,我们需要特别的谨慎——因为我们要检查全部代码看看哪里有用到这个枚举,比如说在switchif中。上面给出的代码就是这种情况。


InputFormat中加了InputFormatEntity——这里是我想象的,因为确实在InputFormat的后面有加入这个常量。很多时候,程序员在枚举后面加了新的常量,然后忘了检查代码以确保他们有正确处理这个常量,也没有修改switch操作。


最后的结果就是,在这个例子中,并没有处理到"m_format==InputFormatEntity"的情况。


正确代码



建议


让我们想想,怎样在代码重构的时候避免这种错误?最简单,但不那么有效的解决方法就是加一个default’,它可以输出一个信息,像这样:



现在,如果变量m_formatInputFormatEntity,我们就可以看到一个异常。这样的处理方法有两个不好的地方:


因为有可能在测试阶段这个错误并没有显现出来(如果在测试的时候,m_format不等于InputFormatEntity),那这个错误就会流入发布版本,以后才会显现——在客户运行时出现。如果要顾客来反映这种问题,真的很糟糕。

如果我们把default考虑为一个错误,那我们就不得不写一个case来解决枚举所有可能的值。这样就很不方便,尤其是当一个枚举中有很多常量的时候。有时,用default来处理不同case真的很方便。

我建议用以下的方法来解决这个问题,我不敢说这个方法很完美,但至少它有解决到问题。


当你定义一个枚举的时候,要确保你也加了一条特殊的注释。你也可以用关键词和枚举名。


例子:


在上面的代码中,当你要改变枚举InputFormat,你就可以直接在项目的源代码中查找ENUM:InputFormat”


如果你是在一个开发者团队中,你可以告诉你的小伙伴们这个约定,然后把它加入到你们的编程标准和风格指引中。如果有人没能遵守这条原则,真遗憾。



我想在看了枚举类型的错误后,你肯定很累了。所以这次,让我们休息一会,不看代码了。


一个典型的场景——你们的项目没有正确运行。但你也不知道发生了什么。在这种情况下,我建议你不要急于去责备某个人,而应该把焦点放到你们的代码上。在99.9%的案例中,罪恶之源就是你们的开发团队中某个人引入了bug。通常这个bug都非常的愚蠢且平淡无奇。所以快点花点时间去找找。


实际上,这个一直出现的bug也不能说明什么。你可能只是遭遇了海森堡bugHeisenbug.


责备编译器也不是什么好主意。它当然也会出错啊,虽然真的非常少。比如说,你会发现就是仅仅错用了sizeof(),这件事就会变得很棘手。我的博客里有篇博文就是关于它的:The compiler is to blame for everything


但为了让记录更公正一点,我应该说,还是会有例外存在的。很少的情况下,bug不对代码做什么。但我们还是应该注意到这种可能性的存在。这可以帮助我们保持理智。


我会用一个我曾经遭遇过的例子来证明这种可能性。非常幸运,我当时有截图。


当时,我在做一个简单的测试项目,想要去演示Viva64分析器(PVS-Studio的前身)的功能,但是它没能正确运行。


经过漫长而令人讨厌的调查之后,我发现是一个内存槽引起这所有的问题。更确切地说,是1bit。你可以看看下面的照片,我当时正在调试,想往这个存储单元写入3’




在改变内存之后,编译器开始读值并将其显示在窗口上,但它显示的是2:看,地址是0x02。尽管我已经把值设置为3了。低位总为0.



一个内存测试项目证实了这个问题。非常奇怪,这台电脑一直都工作得好好的,没出什么问题。最后为了让项目正确运行,我换了内存条。


我真的很幸运。当时我处理的是一个简单的测试项目。虽然我还是花了很多时间去了解发生了什么。我花了两个多小时去检查汇编器目录,想要找到引起这种奇怪行为的原因是什么。是的,我当时有因此责备编译器。


我无法想象,如果是一个实际的项目,我要花多大的时间精力去解决这个问题。谢天谢地,我当时不用去调试其他的东西。


建议


要时刻检查你代码中的错误。别想着推卸责任。


但是,如果一个bug只在你的电脑上出现,而且都快一周了。那么,可能不是因为你的代码。

要持续寻找bug。但是在回家前,可以运行一个一整夜的RAM测试。或许,这个简单的测试步骤能节省你的时间精力。



下面的代码来自Haiku项目(BeOS的继承者)。代码中包含的错误被PVS-Studio诊断为:V696. 'continue'操作会终止'do { ... } while (FALSE)' 循环,因为判断条件一直是false



解释


do-while循环里的continue的运作机制可能跟某些程序员想象的不一样。当遇到continue的时候,会先要检查循环的终止条件。我想要更详细的解释这个问题。假设有个程序员写了这么一段代码:



或者是这样的:



很多程序员凭直觉理解,当遇到continue,就要(重新)评估控制条件(i<n,然后只有为真的时候才会进入下一个循环迭代。但,如果程序员的代码是这么写的:



直觉就不起作用了哦。因为他们没有在continue上面看到判断条件,而且,似乎对于他们来说,continue会马上触发下一个循环迭代。事情并非如此,continue还是像它平常所做的那样——先重新评估控制条件。


除非踩了狗屎运,不然缺乏对continue的理解真的很难不出错。无论如何,如果循环条件 一直是false,这个错误肯定会发生。因为在上面给出的代码中,程序员打算在之后的循环中执行特定的操作。代码中的注释//try again ”证明了他们有这个意图。当然不会有again’啦,因为判断条件一直是false,而且一遇到continue,循环就会终止哦。


换句话说,在这个do {...} while (false)结构中,continue就相当于break


正确代码


要写正确代码有很多选项。比如,创造一个无限循环,然后用continue继续,用break退出。



建议


尝试着不要在do { ... } while (...)continue。即使你真的知道这是如何运作的。因为一不小心你就会犯这种错误,而且/或者你的同时就不能正确理解代码啦,最后把它错改。我一直都这么说:一个优秀的程序员不是知道并使用其他语言技巧的那一个,而是能够写出干净易懂的,即使新手也能理解的代码的那一个。



新的C++标准引入很多有用的改变。有些我现在还没有用到,但是有些应该马上用起来,因为用它们有诸多益处。


其中一个现代化标准就是关键字nullptr, 其旨在代替NULL宏。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
辛苦了
2017-3-10 19:43
0
雪    币: 2325
活跃值: (4903)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
可不可以 都放在一个pdf中·!~
2017-3-10 21:38
0
雪    币: 34
活跃值: (864)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
4
值得怀疑 可不可以 都放在一个pdf中·!~
下午把所有的翻译好之后就集中成一个。
2017-3-11 08:21
0
游客
登录 | 注册 方可回帖
返回
//