5

我一直在查看来自开源项目的一些代码,并注意到在不止一次的情况下,枚举的值是通过将值移位一个递增的位置来分配的。我看不到这样做的任何具体原因,也看不到仅通过增加 +1 来分配值的效率有所提高。

无论如何,如果没有一些代码来证明让我感到困惑的事情,这可能毫无意义。

1级

enum EventType {
        NONE                = 0,
        PUSH                = 1<<0,
        RELEASE             = 1<<1,
        DOUBLECLICK         = 1<<2,
        DRAG                = 1<<3,
        MOVE                = 1<<4,
        KEYDOWN             = 1<<5,
        KEYUP               = 1<<6,
        FRAME               = 1<<7,
        RESIZE              = 1<<8,
        SCROLL              = 1<<9,
        PEN_PRESSURE        = 1<<10,
        PEN_ORIENTATION     = 1<<11,
        PEN_PROXIMITY_ENTER = 1<<12,
        PEN_PROXIMITY_LEAVE = 1<<13,
        CLOSE_WINDOW        = 1<<14,
        QUIT_APPLICATION    = 1<<15,
        USER                = 1<<16
    };

2 级

    enum EventType {
        EVENT_MOUSE_DOUBLE_CLICK = osgGA::GUIEventAdapter::DOUBLECLICK,
        EVENT_MOUSE_DRAG         = osgGA::GUIEventAdapter::DRAG,
        EVENT_KEY_DOWN           = osgGA::GUIEventAdapter::KEYDOWN,
        EVENT_SCROLL             = osgGA::GUIEventAdapter::SCROLL,
        EVENT_MOUSE_CLICK        = osgGA::GUIEventAdapter::USER << 1,
        EVENT_MULTI_DRAG         = osgGA::GUIEventAdapter::USER << 2,   // drag with 2 fingers
        EVENT_MULTI_PINCH        = osgGA::GUIEventAdapter::USER << 3,   // pinch with 2 fingers
        EVENT_MULTI_TWIST        = osgGA::GUIEventAdapter::USER << 4    // drag 2 fingers in different directions
    };

如果我没看错,EventType::USER 的二进制显式值为 65536 或 10000000000000000。EVENT_MULTI_TWIST 的二进制值为 1048576 或 100000000000000000000。

以这种方式分配枚举值的目的是什么,而不是像这样:

enum EventType {
        NONE                = 0,
        PUSH                = 1,
        RELEASE             = 2,
        DOUBLECLICK         = 3,
        DRAG                = 4,
        MOVE                = 5,
        KEYDOWN             = 6,
        KEYUP               = 7,
        FRAME               = 8,
        RESIZE              = 9,
        SCROLL              = 10,
        PEN_PRESSURE        = 11,
        PEN_ORIENTATION     = 12,
        PEN_PROXIMITY_ENTER = 13,
        PEN_PROXIMITY_LEAVE = 14,
        CLOSE_WINDOW        = 15,
        QUIT_APPLICATION    = 16,
        USER                = 17
    };
4

3 回答 3

9

这样做的通常原因是将每个枚举值与最终 v 值的单个位相关联,因此您可以(例如)将多个标志编码到单个变量中。

例如,对于典型的 32 位系统,您可以(显然)将 32 个单独的标志编码为单个 32 位int(或者,最好是unsigned int)。

例如,如果您正在查看键盘上的键,您可以将“普通”键(如字母和数字)编码为一个字节(可能使用连续值,正如您所建议的那样),以及“修饰符”键(如shiftaltcontrol作为单个位。这将允许您(例如)将++control之类的内容编码为单个值。altA

同样,对于鼠标消息,您可以将鼠标按钮视为“修饰符”,因此您可以将诸如拖动鼠标之类的东西编码为单个值。

在这两种情况下,将“修饰符”编码为单个位的重要一点是,这允许您以后明确检索这些修饰符——如果在值中设置了正确的位,则使用该修饰符。相比之下,如果你只使用连续的数字,你就不能在之后提取单个片段。例如,如果您的输入编码为1,23,您无法判断 a3是打算表示对应于 的原始输入3,还是同时表示两者1的输入2。但是,如果您将值编码为和1,则可以组合值并仍然对它们进行解码,这样您就可以准确地看到产生特定值所需的输入。24

于 2013-09-17T04:15:52.103 回答
3

这是其中一种情况,因为可以在 C++ 中实现事物,所以没有添加显式实现它的机制。

枚举提供链接器/调试器可见的常量,并且可以限定为类和模板,因此虽然它并不能完全实现最终用户想要实现的目标,当然也没有明确地做到这一点,但事实上枚举表达式不必是顺序的,这意味着它被认为足以实现枚举位掩码。

结果值可以直接在位掩码中使用,例如:

    enum {
        Widget = 1 << 0,  // value b00000001
        Dingo = 1 << 1,   // value b00000010
        Herp = 1 << 2     // value b00000100
    };

    if (flag & Widget)
        doWidgetThings();
    if (flag & Dingo)
        feedTheDog(baby);
    if (flag & Herp)
        win();

这能够一次在“flag”中接受 0、1 或多个值:

    flag = 0; // matches none
    flag = Herp; // matches one flag.
    flag = Widget | Dingo; // sets flag to have both widget and dingo.
于 2013-09-17T04:17:41.623 回答
2

您想要这样做的原因是因为您可以使用这种方式定义的枚举作为位掩码。给定一个 32 位整数,您可以拥有 32 个独特的特征,这些特征都可以使用枚举值的按位组合来启用或禁用。

例如,如果您想创建一个事件处理程序来侦听QUIT_APPLICATIONCLOSE_WINDOW在使用位移的实现中,您可以简单地使用QUIT_APPLICATION | CLOSE_WINDOW. 每个枚举都是一个位,将它们组合在一起使您可以轻松地指定所需的每种类型的事件,而如果只是从 1-16 枚举它们则无法做到。

考虑一下您想要一个事件处理程序来侦听PUSHRELEASE使用您提出的“更有效”枚举的情况。如果您将这些 OR 放在一起,您将得到3. 3也是DOUBLECLICK您示例中的值。所以没有办法知道你真正想要哪些事件;是和的结合PUSH还是RELEASE简单的DOUBLECLICK?这就是为什么为每个枚举类型保留一个位是如此强大的原因。

你会经常看到这样定义的枚举值:

enum EventType {
    NONE                =       0,
    PUSH                =     0x1,
    RELEASE             =     0x2,
    DOUBLECLICK         =     0x4,
    DRAG                =     0x8,
    MOVE                =    0x10,
    KEYDOWN             =    0x20,
    KEYUP               =    0x40,
    FRAME               =    0x80,
    RESIZE              =   0x100,
    SCROLL              =   0x200,
    PEN_PRESSURE        =   0x400,
    PEN_ORIENTATION     =   0x800,
    PEN_PROXIMITY_ENTER =  0x1000,
    PEN_PROXIMITY_LEAVE =  0x2000,
    CLOSE_WINDOW        =  0x4000,
    QUIT_APPLICATION    =  0x8000,
    USER                = 0x10000
};

有时您甚至会看到以下内容:

    ...
    CLOSE_WINDOW        =  0x4000,
    QUIT_APPLICATION    =  0x8000,
    CLOSE_AND_QUIT      =  CLOSE_WINDOW | QUIT_APPLICATION,
    ...

位移符号只是更容易阅读(对于某些人来说),它们都做同样的事情。每个枚举值表示单个位或位的离散组合(除了NONE,它表示所有位的缺失)。

于 2013-09-17T04:12:04.767 回答