使用枚举的一些好例子

当我学习C时,我学会了枚举 ,并不时地提醒自己,并且大多数时候通过从某些来源重新阅读,我发现这是因为我从未在编程中使用它,我的编程兴趣集中在算法问题解决上,所以我不确定在哪里可以使用枚举。

有人可以提出一些很好的例子,使用枚举可以让事情变得简单吗?

我会赞赏算法示例,但欢迎算法或非算法示例。

想象一下,您正在编写深度优先搜索,并且您希望标记边缘是否为树,后,前或交叉。 您可以使用四种可能性创建枚举EDGE_TYPE,并使用它来标记边缘。

在描述/观察某个系统的某些属性时,您可能会发现该属性可以包含有限集中的任何值。 命名这些值,为每个值分配一个整数值(代码),在枚举中收集它们,并定义该属性的类型 。 考虑该属性的所有操作现在都可以使用此类型。

示例:对于某些系统,我们可以将其状态视为其属性之一。 我们可以观察它并说它可以处于“未初始化”状态,“初始化”状态,“活动”或“空闲”状态(可以在这里添加更多状态……)。 如果要在该系统上执行某些操作但取决于当前状态,您将如何将状态信息传递给该操作(函数)? 您可以传递字符串’未初始化’,’初始化’…但是如果您只从集合中传递一个整数,那么更高效,简单且安全的错误就是:

 enum State { Uninitialized, Initialization, Active, Idle }; 

该函数将State作为参数,并且在根据当前状态决定做什么时可以使用switch:

 void foo(..., const State state,...) { ... switch(state) { case Uninitialized: cout << "Uninitialized" << endl; break; case Initialization: ... } ... } 

使用枚举类型来描述一组有限的属性值比使用一组#defines和整数变量更安全。 例如,如果你有:

 #define UNINITIALIZED 0 #define INITIALIZATION 1 #define ACTIVE 2 #define IDLE 3 

 int nState; 

没有什么可以阻止你为nState分配任何整数值:

 nState = 4; // What state is 4? 

如果使用枚举:

 State state; 

你不能为它分配一个任意整数值但只能分配一个枚举器(虽然枚举的基础类型是整数! - 见这个 ):

 state = Active; 

我使用它们作为函数的参数,而不是使用布尔值来提高我的代码的可读性。

看一下维基百科关于枚举的基本原理 。

值得一提的是: 枚举类型 – 枚举

枚举的一个用途是在呼叫站点使代码更清晰。 相比:

 //Usage: Kick(Dog); enum PetType { Cat, Dog }; void Kick(PetType p) { switch(p) { case Cat: //Kick Cat break; case Dog: //Kick Dog break; default: //Throw an exception. break; } } 

 //Usage: Kick(false); void Kick(bool isCat) { if (isCat) { //Kick Cat } else { //Kick Dog } } 

即使布尔值也能正常工作,不熟悉函数的人将需要更加努力地确定在使用布尔值的情况下它做了什么。 Kick(Dog)Kick(false)更清楚。

所有关于使用枚举作为符号常量的说法,我想强调的是,在C ++中使用类中的枚举提供了一种非常好的,可读的和方便的封装和暴露类function的方法,例如

 class BlockCipher { public: enum PaddingOptions { NO_PADDING, DEFAULT_PADDING, ZERO_PADDING /*...*/ } void encrypt(const std::string& cleartext, std::string& ciphertext, PaddingOptions pad=DEFAULT_PADDING); }; int main() { std::string clear("hello, world"); std::string encrypted; BlockCipher encryptor; encryptor.encrypt(clear, encrypted, BlockCipher::NO_PADDING); } 

enum可以使代码更容易阅读,并且可以在编译期间提供更好的类型检查。

枚举问题

  1. 它们可以转换为intunsigned int并分配给那些变量,从而在类型检查中创建一个漏洞。
  2. 他们的符号名称无法直接打印。 将enum传递给std::cout导致enum转换为整数然后打印出来。 大多数实现必须执行表查找以在打印之前将enum转换为文本。

备择方案

enum另一种替代方法是使用字符串。 我曾经在商店工作,他们传递的是常量字符串而不是枚举。 一个优点是命名值始终可用,即使调试符号不可用。 此外,打印时无需转换。

字符串的一些缺点:

  1. 不能在switch语句中使用。
  2. 比较时的区分大小写。
  3. 比较可能需要更多的执行时间。
  4. 占用更多数据或可执行空间。

枚举比#define有一个优势,但它纯粹是一个实现细节:调试器通常可以显示/使用enum值但#define d值。

另一方面,# #define有几个基本优点,其中之一就是你可以用#ifdef来测试它的存在。 如果您需要支持库的多个版本,并且希望在可用的情况下选择使用新的类似枚举的选项,这将非常有用。

一些库作者使用混合方法,首先使用enum定义常量:

 #define FOO FOO #define BAR BAR 

等等

您可以在这些值之间具有第一个值和最后一个值以及其他所有值。 代码中的每个位置都会检查您的值是否在范围内。 现在在第一个和最后一个之间为你的枚举添加新值,并且不会改变所有这些检查!

 typedef enum { First_value, Uninitialized, Initialization, Active, Idle, Last_value } my_type; void function(my_type state) { if ((state > First_value) && (state < Last_value)) { //Do stuff... } } 

我开始了一个个人项目,我想识别我的数据包ID,它看起来像这样:

 enum{ //Client to server //Connection ID_KEEP_ALIVE = 0x00, ID_LOGIN_REQUEST = 0x01, ID_CONNECTING = 0x02, ID_DISCONNECT = 0x03, //Player actions ID_PLAYER_INFO = 0x04, ID_PLAYER_MOVE = 0x05, ID_PLAYER_ATTACK = 0x06, //Inventory ID_LOOT_ITEM = 0x10, ID_DESTROY_ITEM = 0x12, ID_USE_ITEM = 0x13, ID_EQUIP_ITEM = 0x15, ID_UNEQUIP_ITEM = 0x16, ID_DROP_ITEM = 0x17, }; 

然后,当我收到一个数据包时,我有一个巨大的开关,看起来像这样处理数据包并发送它们:

 switch(packet.packetID){ case ID_KEEP_ALIVE: //... break; case ID_LOGIN_REQUEST: //... break; case ID_CONNECTING: //... break; case ID_DISCONNECT: //... break; //.. } 

这是我最好的例子,享受:)