翻译《有关编程、重构及其他的终极问题?》——15.在你的代码中开始使用enum class吧
标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年02月01日
15.在你的代码中开始使用enum class吧
抱歉,所有我这里关于这个问题的例子都很大,所以我选择了一个最小的,其实还是有些大。
这个bug实在Source SDK库中发现的。PVS-Studio是这样描述这个问题的:V556 The values of different enum types are compared: Reason == PUNTED_BY_CANNON(译者注:大意是说来自不同enum中的值被比较了)。
enum PhysGunPickup_t
{
PICKED_UP_BY_CANNON,
PUNTED_BY_CANNON,
PICKED_UP_BY_PLAYER,
};
enum PhysGunDrop_t
{
DROPPED_BY_PLAYER,
THROWN_BY_PLAYER,
DROPPED_BY_CANNON,
LAUNCHED_BY_CANNON,
};
void CBreakableProp::OnPhysGunDrop(...., PhysGunDrop_t Reason)
{
....
if( Reason == PUNTED_BY_CANNON )
{
PlayPuntSound();
}
....
}
解释
Reason变量是一个PhysGunDrop_t的enum类型,而与这个变量做比较的常量PUNTED_BY_CANNON却属于另外一个enum类型,所以很明显这是一个逻辑错误。
这个bug非常常见,我甚至无意中在如下项目中发现类似bug:Clang,TortoiseGit,and Linux Kernel。
这个bug如此常见的原因是因为在标准C++中enum类型不是类型安全的,所以你可能很容易就混淆那些是可以比较的。
正确的代码
我们无法确定这段代码正确的版本是怎样的。我猜PUNTED_BY_CANNON应该可以被DROPPED_BY_CANNON或者LAUNCHED_BY_CANNON替换。我们这里就用LAUNCHED_BY_CANNON吧。
{
PlayPuntSound();
}
建议
如果你使用C++,你应该决定幸运。我建议你马上就开始使用enum class吧(译者注:C++11标准中出现的,大多数编译器都支持),那样的话编译器不会让你比较来自不同enum类型的值。你就不再会比较磅和寸了。
有一些C++的创新我不是很有信心。比如auto关键字,我相信如果使用太多的话反而会不好,因为我觉得:程序员们其实在阅读代码时花费的时间要比写代码还多,所以我们必须要保证代码容易读。在C语言中,变量在函数的开头定义,所以当我们在代码中间或者结尾编辑做修改时,不太容易一下子发现类似Alice变量意味着什么这样这类的问题,所以就有了变量命名的一个规则方式。比如,有前置规则,这样pfAlice可能就表明是一个“指向浮点的指针”。
在C++中,你可以随意声明你的变量并且这样是被认可的好方式。但继续使用前置或后置规则命名变量却不再流行。然后,auto关键字又出现了,导致了编程时各种神秘的结构又出现了,比如:“auto Alice = Foo();”,谁他妈的知道Alice适合呢么?!(译者注:这里作者有F**k的字眼,我就贴近的翻译了)。
抱歉离题了,我只是想顺便给你说下语言的新特性可能同时意味着好和不好,但这不适用于enum class:我相信这个新特性只意味着好。
当我们使用enum class时,你必须清晰的指定某一常量属于哪一个enum类型,这就防止了代码出现之前提到的问题。使用enum class后,代码如下:
enum class PhysGunDrop_t
{
DROPPED_BY_PLAYER,
THROWN_BY_PLAYER,
DROPPED_BY_CANNON,
LAUNCHED_BY_CANNON,
};
void CBreakableProp::OnPhysGunDrop(...., PhysGunDrop_t Reason)
{
....
if( Reason == PhysGunDrop_t::LAUNCHED_BY_CANNON )
{
PlayPuntSound();
}
....
}
的确,修改旧的代码不容易,但我强烈建议从今天起在你的代码中开始使用enum class类型,你的代码将会受益。
这里我没有对enum class做更多的介绍,所以这里有一些链接,可以方便你学习C++ 11中这个不错的特性中的所有的细节:
1. Wikepedia C++11:Strongly typed enumerations。
2. Cppreference:Enumeration declaration。
3. StackOverflow:Why is enum class prefered over plain enum?