C++小知识:尽可能使用枚举类

所属分类: 软件编程 / C 语言 阅读数: 82
收藏 0 赞 0 分享

静态代码分析工具可简化编码过程,检测出错误并帮助修复。PVS-Studio 是一个用于 C/C++ 的静态代码分析工具。该团队检测了 200 多个 C/C++ 开源项目,包括了 Unreal Engine、Php、Haiku、Qt 和 Linux 内核等知名项目。

下面这个 Bug 是在 Source SDK 的源代码中发现的。

错误代码:

这种错误的例子代码量都非常大,我尽可能地选取其中最小的一部分,但是很抱歉,代码看起来依旧很冗长。

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,却用它和属于另一个枚举类型的常量作比较,这种比较显然是个逻辑错误。

但是这种 bug 模式很普遍,我甚至在像 Clang、TortoiseGit 和 Linux Kernel 这种项目中都有碰到过。

为什么会如此频繁?因为在标准C++中,枚举类型本来就不是类型安全的。到底什么该跟什么做比较,很容易让人混淆。

正确代码:

我不太确定这段代码的正确版本应该是什么样的,我猜想 PUNTED_BY_CANNON 应该用 DROPPED_BY_CANNON 或者 LAUNCHED_BY_CANNON 来替代。此处就用 LAUNCHED_BY_CANNON 来代替了。

if( Reason == LAUNCHED_BY_CANNON )
{
 PlayPuntSound();
}

建议:

如果你是用C++写代码,还没碰到过这种bug,算你幸运;我强烈建议你从现在开始在代码中使用“枚举类”。

对于C++11中的一些新特性,我没有太多信心。就拿auto关键字来说吧,我相信如果频繁使用的话,会有很多坏处的。 我是这么看的:比起写代码,程序员会花更多的时间阅读代码,所以我们必须确保程序的可读性很强。 C语言中, 所有变量都必须在函数的一开始就声明,那么在函数的中间或者末尾编辑代码时,没那么容易推测出某个Alice变量到底是什么意思。这也是为什么变量的命名规范会这么多样化? 例如,前缀命名法PfAlice就代表指向浮点数的指针。

C++中你可以随时随地声明变量,这是一种很好的编码风格。因而使用前后缀命名也不再那么受欢迎了。接着auto关键字出现了,直接导致程序员又开始使用各种各样很难理解的构造形式, 诸如auto Alice= FOO(); 之类的。 Alice?谁是TM的Alice?(为保留原文本意,此处翻译稍有不文明)

很抱歉,又偏离我们的主题了。 我想告诉大家的是一些新的特性都有它的好坏两面性。 但是对于“枚举类”, 我坚信使用它有百利而无一害。

在使用枚举类的时候,必须明确指出指定的常量属于哪个枚举类型,以免在代码中出现错误。使用枚举类更新后的代码如下:

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();
 }
 ....
}

说真的,修复旧代码的确会有一定困难,但我强烈推荐你们在代码中使用枚举类,你的项目定会从中受益。

我觉得在这儿详细介绍枚举类没有多大意义,有兴趣的可以自己去了解一下的。

这个错误是用静态代码分析工具 PVS-Studio 检测到的,错误信息为:V556 对不同枚举类型的值进行比较:Reason == PUNTED_BY_CANNON

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

更多精彩内容其他人还在看

C++中四种对象生存期和作用域以及static的用法总结分析

以下是对C++中四种对象生存期和作用域以及static的用法进行了详细的介绍,需要的朋友可以过来参考下
收藏 0 赞 0 分享

C++嵌套类与局部类详细解析

从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类之外的作用域使用该类名时,需要加名字限定
收藏 0 赞 0 分享

C++空类详解

以下是对C++中的空类进行了详细的介绍,需要的朋友可以过来参考下
收藏 0 赞 0 分享

C++之友元:友元函数和友元类详解

友元是一种允许非类成员函数访问类的非公有成员的一种机制。可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元
收藏 0 赞 0 分享

C++中返回指向函数的指针示例

int (*ff(int)) (int *,int);表示:ff(int)是一个函数,带有一个int型的形参,该函数返回int (*) (int *,int),它是一个指向函数的指针,所指向的函数返回int型并带有两个分别是Int*和int型的形参
收藏 0 赞 0 分享

C数据结构之单链表详细示例分析

以下是对C语言中的单链表进行了详细的分析介绍,需要的朋友可以过来参考下
收藏 0 赞 0 分享

C数据结构之双链表详细示例分析

以下是对c语言中的双链表进行了详细的分析介绍,需要的朋友可以过来参考下
收藏 0 赞 0 分享

浅析如何在c语言中调用Linux脚本

如何在c语言中调用Linux脚本呢?下面小编就为大家详细的介绍一下吧!需要的朋友可以过来参考下
收藏 0 赞 0 分享

深入解析unsigned int 和 int

以下是对unsigned int和int进行了详细的分析介绍,需要的朋友可以过来参考下
收藏 0 赞 0 分享

浅谈C++中的string 类型占几个字节

本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节
收藏 0 赞 0 分享
查看更多