标志,枚举(C)

标志,枚举(C),c,enums,flags,C,Enums,Flags,我不太习惯使用标志进行编程,但我认为我刚刚发现了一种使用标志的情况: 我有几个对象,它们将自己注册为特定事件的侦听器。它们注册的事件取决于构造它们时发送给它们的变量。我认为这样做的一个好方法是发送按位或连接的变量,如:TAKES|DAMAGE | GRABBABLE | LIQUID,等等。然后,在构造函数中,对象可以检查设置了哪些标志,并将其自身注册为已设置标志的侦听器 但这就是我困惑的地方。优选地,标志将位于枚举中。但这也是一个问题。如果我们有这些旗帜: enum { TAKES_D

我不太习惯使用标志进行编程,但我认为我刚刚发现了一种使用标志的情况:

我有几个对象,它们将自己注册为特定事件的侦听器。它们注册的事件取决于构造它们时发送给它们的变量。我认为这样做的一个好方法是发送按位或连接的变量,如:
TAKES|DAMAGE | GRABBABLE | LIQUID
,等等。然后,在构造函数中,对象可以检查设置了哪些标志,并将其自身注册为已设置标志的侦听器

但这就是我困惑的地方。优选地,标志将位于
枚举中。但这也是一个问题。如果我们有这些旗帜:

enum
{
    TAKES_DAMAGE,/* (0) */
    GRABBABLE, /* (1) */
    LIQUID, /* (2) */
    SOME_OTHER /* (3) */
};
然后发送标志“其他”将与发送“可抓取液体”相同,不是吗


如何准确地处理这些内容?

您应该使标记仅为两个幂,即,无论您存储这些标记的数据类型是什么,每个都是一个位,并且在按位或时没有重叠。

您的枚举需要是两个幂:

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};
或者以更具可读性的方式:

enum
{
    TAKES_DAMAGE = 1 << 0,
    GRABBABLE = 1 << 1,
    LIQUID = 1 << 2,
    SOME_OTHER = 1 << 3
};
。。。如果枚举值如下所示,则该选项有效:

 TAKES_DAMAGE: 00000001
 GRABBABLE:    00000010
 LIQUID:       00000100
 SOME_OTHER:   00001000
因此,假设您已将
myVar
设置为
GRABBABLE | TAKES_DAMAGE
,以下是您需要检查GRABBABLE标志时的工作方式:

 myVar:     00000011
 GRABBABLE: 00000010 [AND]
 -------------------
            00000010 // non-zero => converts to true
如果将
myVar
设置为
LIQUID | SOME_OTHER
,则该操作将导致:

 myVar:     00001100
 GRABBABLE: 00000010 [AND]
 -------------------
            00000000 // zero => converts to false
你需要

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};

你不能只设置枚举中的值吗

enum {
 TAKES_DAMAGE = 1,
 GRABBABLE    = 2,
 LIQUID       = 4
}

之后,只需按位或按位执行即可。

是。相反,将枚举成员的幂设置为2:

enum
{
    TAKES_DAMAGE = (1 << 0),
    GRABBABLE = (1 << 1),
    LIQUID = (1 << 2),
    SOME_OTHER = (1 << 3)
};
enum
{

TAKES_DAMAGE=(1另一种存储标志的方法是根本不考虑基础类型。当使用枚举时,枚举值默认存储到一个无符号int中,在普通计算机上为32位。这只提供32个可能的标志:当然很多,但在某些情况下它是不够的

现在,您可以通过以下方式定义标志集:

typedef struct
{
    int takes_damage : 1;
    int grabbable    : 1;
    int liquid       : 1;
    int some_other   : 1;
} flags;
如果从未遇到过这种情况,则“:1”部分会告诉编译器仅使用1位来存储此结构成员

现在,您可以定义一个变量来保存这些标志,并使用这些标志:

flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other

myflags.liquid = 1; // change the flags to include the liquid

if ( myflags.takes_damage ) // test for one flag
    apply_damage();
if ( myflags.liquid && myflags.some_other ) // test for multiple flags
    show_strange_behavior();

此方法允许您定义任意数量的标志,不受限制,并且您可以随时扩展标志集,而无需担心溢出。缺点是测试标志子集更麻烦,需要更多代码。

@jldupont-我相信您的意思是“移位运算符”?8v)不要认为我以前已经得到了5个回复。谢谢你的精彩解释和解决方案。现在我有了一个新的方式来谈论C++流的插入和提取:<代码> cOutt。如果你不喜欢移位运算符方法,最好使用十六进制而不是十进制:TaksYffist= 0x001,Gabababy= 0x002,LIQ。UID=0x0004,GAS=0x0008,PUSHABLE=0x0010,造成_-DAMAGE=0x0020,OTHER=0x0040等。这样,所有值在代码中对齐后,它看起来都很漂亮。向下投票人:也许你想解释一下你发现这个答案没有用的地方?我认为你需要修正这个答案的语法。(我知道,问题中也有同样的错误。不过现在已经解决了。)有趣。我第一次遇到这种方法。我会有兴趣阅读比我更有知识的人关于两个枚举的传统功能的优缺点的评论。它适用于所有编译器和所有架构吗?陷阱是什么?@gregschlom我也会有兴趣阅读更多的评论。这是totally标准兼容,因此它应该与任何编译器一起工作。我能想到的唯一缺陷是:1.如果结构未打包,标志集可能占用大量内存,其中大部分未使用。2.您不能一次指定多个标志值,如
myflags=grabbable&liquid
(不过,这可以通过使用包含struct和int值的并集来实现,但这会施加更多的约束,更依赖于编译器,并且不是本文的主题)谢谢!另一个问题(正如您指出的,这与无法测试标志子集有关),就是你不能很容易地测试两个标志变量是否相等。(即:你不能做:if(myflags==someOtherFlags){…})。对我来说有点阻碍,我要坚持使用普通的旧枚举…C标准在哪里说枚举是无符号的?C99 6.4.4.3p2说“声明为枚举常量的标识符具有int类型”。使用宽度为1的有符号位域仅适用于两种互补实现。在符号幅度实现中,所有可能的值都是0。甚至可能存在不包含所有零位的值的陷阱表示。
flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other

myflags.liquid = 1; // change the flags to include the liquid

if ( myflags.takes_damage ) // test for one flag
    apply_damage();
if ( myflags.liquid && myflags.some_other ) // test for multiple flags
    show_strange_behavior();