2009-10-27 121 views
11

我不是很习惯用旗帜编程,但我想我刚刚发现的情况下,他们会是有用的:标志,枚举(C)

我有一对夫妇的对象是自己注册作为某些事件的听众。他们注册哪些事件取决于构建时发送给他们的变量。我认为一个很好的方法是发送按位或连接变量,如:TAKES_DAMAGE | GRABBABLE | LIQUID等。然后,在构造函数中,对象可以检查设置了哪些标志并将它自己注册为侦听器。

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

enum 
{ 
    TAKES_DAMAGE,/* (0) */ 
    GRABBABLE, /* (1) */ 
    LIQUID, /* (2) */ 
    SOME_OTHER /* (3) */ 
}; 

然后发送标志SOME_OTHER(3)将是相同的发送GRABBABLE | LIQUID,不是吗?

你究竟如何处理这个东西?

回答

45

你列举需要成为两个大国:

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 
}; 

为什么?因为你希望能够用标志没有重叠结合,也能够做到这一点:

if(myVar & GRABBABLE) 
{ 
    // grabbable code 
} 

...如果枚举值看起来像这里面的工作原理:

TAKES_DAMAGE: 00000001 
GRABBABLE: 00000010 
LIQUID:  00000100 
SOME_OTHER: 00001000 

所以,说你已经设置myVarGRABBABLE | TAKES_DAMAGE,这里的工作原理是,当你需要检查的GRABBABLE标志:

myVar:  00000011 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000010 // non-zero => converts to true 

如果要设置myVarLIQUID | SOME_OTHER,该操作将导致:

myVar:  00001100 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000000 // zero => converts to false 
+0

@jldupont - 我相信你的意思是“shiFt运营商”? 8v) – 2009-10-27 14:48:12

+0

@jldupont我认为你错过了一封信... – Greg 2009-10-27 14:48:28

+0

不要以为我同时收到5个回复。 o.o 感谢您的出色解释和解决方案。 – quano 2009-10-27 14:48:57

4

您应该只对两个标志赋予权力,即在您存储该标志的任何数据类型中,每个标志都有一点位,并且在按位OR时没有任何重叠。

3

你需要

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

我认为你需要修复这一个的语法。 (我知道,同样的错误是在这个问题上,但现在已经被修复了。) – 2009-10-27 15:01:14

+0

@fred larson,修正:-) – Fredou 2009-10-27 15:15:56

4

你就不能设置的值在枚举?

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

之后,只是按比特或对他们进行处理。

5

是的。相反,使你的枚举成员的权力2:

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

对于downvoter:或许你想解释一下你发现对这个答案没有用处? – 2009-10-27 14:55:06

25

存储标志的另一种方式是根本不打扰底层类型。当使用枚举时,枚举值默认存储在一个无符号整数,这是一个普通计算机上的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(); 

这种方法允许你定义任意数量的标志,没有限制,你可以扩展你的标记设置在任何时候都不用担心溢出。缺点是测试标志的一个子集比较麻烦并且需要更多的代码。

+2

有趣。我第一次遇到这种方法。与传统的两枚枚举的力量相比,我会对阅读更多知识渊博的人的评论感兴趣,而不是比我更多地了解其优点和缺点。它适用于所有的编译器和所有体系结构吗?什么是陷阱? – gregschlom 2011-11-04 12:09:31

+0

@gregschlom我也会对更多评论感兴趣。它完全符合标准,因此它应该可以与任何编译器一起工作。我能想到的唯一缺陷是:1.如果结构没有封装,标志集可能会占用大量内存,其中大部分未使用。 2.你不能一次指定多个标志值,比如'myflags = grabbable&liquid'(但是,这可以通过使用包含struct和int值的联合来实现,但是这会产生更多的约束,更多的依赖于编译器并不是这篇文章的主题) – 2011-11-04 12:26:45

+0

谢谢!另一个问题(与您不能测试标志子集的事实有关,就像您指出的那样)是因为您不容易测试两个标志变量是否相等。 (即:你不能这样做:if(myflags == someOtherFlags){...})。对我来说是一个阻挡者,要坚持使用普通的旧枚举...... – gregschlom 2011-11-10 22:39:42