【问题标题】:Understanding enum members' initialization with bitewise operators使用逐位运算符了解枚举成员的初始化
【发布时间】:2014-08-17 22:07:43
【问题描述】:

我目前正在阅读“SFML 游戏开发”一书以及第 4 章 - 输入处理子章节“接收器类别”第 n°2 段;我们有这个:

“我们定义一个枚举来引用不同的类别。除了 None 之外的每个类别都用一个整数初始化,其中一位设置为 1 ,其余设置为 0 :”


namespace Category
{
   enum Type
   {
     None = 0,
     Scene = 1 << 0,
     PlayerAircraft = 1 << 1,
     AlliedAircraft = 1 << 2
     EnemyAircraft = 1 << 3,
   };
}

一般来说,我对位运算符和二进制运算不太满意;所以我不明白“除了 None 之外的每个类别都用一个整数初始化,其中一位设置为 1 ,其余的设置为 0 :”。

如果每个类别(除了None)都如上所述初始化,“其余”设置为0是什么?!

注意:经过仔细阅读,我想我明白枚举的每个成员都是一个字节,因此,其中的第一位设置为 1,同一字节的其他位是设置为 0。 所以,Scene = 0b1,PlayerAircraft = 0b10(一个位 = 1,另一个 = 0)等等......所以如果我写:PlayerAircraft = 2 &lt;&lt; 1 PlayerAircraft 会等于 0b11?我是对的还是我遗漏了什么?

在本章中进一步介绍;我们有一个if 条件检查请求的类别是否与场景节点一相同;不偏离主题;我不明白那部分。 因为它使用AND(或&amp;,如果您愿意)按位运算符;它如何检查场景节点类别是否与请求的类别相同? 我在 Wikipedia 上查看了它是如何工作的,但我没有完全明白。

这是代码

void SceneNode::onCommand(const Command& command, sf::Time dt)
{
   if(command.category & getCategory()) //The part I don't understand
     command.action(*this, dt);
/* ... */
}

我不明白...

Voilà,提前谢谢你,我希望我在这里的第一篇文章不会太乱,并且我提供了足够的信息。 如果没有,我会编辑! :)

PS:对不起英语蹩脚,我今天还没醒。

【问题讨论】:

  • 1&lt;&lt;0 计算结果为 1 (00000001)。 1&lt;&lt;1 计算结果为 2 (00000010)。 1&lt;&lt;2 计算结果为 4 (00000100)。 1&lt;&lt;3 计算结果为 8 (00001000)。它是位移,您可以将其视为分别通过左/右移位乘以/除以2。我假设正在这样做,以便他们稍后可以在此枚举中使用 bitmask

标签: c++ struct enums bitwise-operators


【解决方案1】:

operator&lt;&lt; 是什么?

在这种情况下,了解operator&lt;&lt; 非常容易。运算符左侧是当前值对象;右边是我们应该执行多少次左移。

例如,给定一个对应于1的字节:

0 0 0 0 0 0 0 1

向左移动一次会导致:

0 0 0 0 0 0 1 0

现在,如果我们将1 视为2 ^ 0,则在每次左移时,我们都会增加指数。因此上面的字节等于2 ^ 1等等:

0 0 0 0 0 0 0 1 // 2 ^ 0 = 1
0 0 0 0 0 0 1 0 // 2 ^ 1 = 2
0 0 0 0 0 1 0 0 // 2 ^ 2 = 4
0 0 0 0 1 0 0 0 // 2 ^ 3 = 8
...

operator&amp; 是什么?

二进制 operator&amp; 是按位与。对于两个位集的每个对应位,如果两个位都为1,则结果位为1,否则为0。您可以使用它来检查在给定的位集中是否存在特定类别。例如,让我们考虑类别:

0 0 0 0 0 1 0 0

让我们考虑一个代表类别 1 和 2,但不是我们的类别 3 的位集:

0 0 0 0 0 0 1 1

两者之间的按位与将给出0(可隐式转换为false):

0 0 0 0 0 1 0 0 &
0 0 0 0 0 0 1 1 =
0 0 0 0 0 0 0 0

另一方面,如果我们的位集(现在代表第一和第三类别)包含我们的类别:

0 0 0 0 0 1 0 0 &
0 0 0 0 0 1 0 1 =
0 0 0 0 0 1 0 0

您将有一个表示与0 有点不同的位集(因此可以隐式转换为true)。

结论

如果您将每个类别表示为位集中的单个位,则可以轻松地在单个位集中表示一组类别。

假设我们要表示四个字母ACGT。我们可以在长度为 4 的位集中分配一个位:

0 0 0 1 // A
0 0 1 0 // C
0 1 0 0 // G
1 0 0 0 // T

现在让我们创建一个表示字母AG 的位集:

0 1 0 1 // A + G

我们可以检查给定的字母是否在通过&amp; 设置的位中。

A吗?

0 1 0 1 & // A + G
0 0 0 1 = // A
0 0 0 1   // 1 ~ true

是的,有。有C吗?

0 1 0 1 & // A + G
0 0 1 0 = // C
0 0 0 0   // 0 ~ false

不。有G吗?

0 1 0 1 & // A + G
0 1 0 0 = // G
0 1 0 0   // 4 ~ true

是的,有。最后,有T吗?

0 1 0 1 & // A + G
1 0 0 0 = // T
0 0 0 0   // 0 ~ false

不,没有。

一般

一般来说,给定一个位集a 和位集b 用于我们要检查在a 中是否存在的类别,&amp; 的结果只能有两种:

  1. 0
  2. 分配给类别的值(2 的幂)

在 C++ 中,测试:

if (a & b)

也可以指定为:

if ((a & b) == a)

实际例子

现在您应该能够理解给定enum 之类的:

enum type
    { none              = 0
    , scene             = 1 << 0
    , player_aircraft   = 1 << 1
    , allied_aircraft   = 1 << 2
    , enemy_aircraft    = 1 << 3 };

还有这些变量:

auto a = scene;
auto b = enemy_aircraft;
auto c = player_aircraft;

以下内容:

    std::cout << "a is of type: " << ((a & scene) ? "scene" : "not scene") << '\n';    
    std::cout << "b is of type: " << ((b & enemy_aircraft) ? "enemy_aircraft" : "not enemy_aircraft") << '\n'; 
    std::cout << "c is of type: " << ((c & player_aircraft) ? "player_aircraft" : "not player_aircraft") << '\n'; 

将打印:

a 的类型是:场景

b 的类型是:enemy_aircraft

c 是类型:player_aircraft

Live demo

【讨论】:

【解决方案2】:

这并不是真正的初始化 - 枚举项是编译时常量,按位表达式是在编译时计算的。

每个成员的二进制表示将是:

 None           : 00000000
 Scene          : 00000001
 PlayerAircraft : 00000010
 AlliedAircraft : 00000100
 EnemyAircraft  : 00001000

虽然实际位数会更大 - 我只显示了最低有效 8 位 其余的将为零。

当一个值可以同时表示多个值时,可以使用这种类型的枚举——例如PlayerAircraft | Scene的组合具有唯一值00000011;尽管PlayerAircraft | Scene 的类型不是int 而不是Type,但在这种情况下并不清楚这是位掩码的用途。

移位常量表达式的使用可能只是为了清楚地表明为每个枚举设置了一个位。你也可以这样写:

 None           = 0x00,
 Scene          = 0x01,
 PlayerAircraft = 0x02,
 AlliedAircraft = 0x04,
 EnemyAircraft  : 0x08

通常使用十六进制表示法,因为它是二进制值的简洁表示法,因为一位数字正好对应于 4 个二进制数字。

要测试是否设置了二进制值中的单个位,请按位-& 使用要测试的掩码的值。例如:

value      : 1001
Scene Mask : 0001
-----------------
         & : 0000  // Non-zero - value Scene bit is set


value       : 1001
Player Mask : 0010
------------------
          & : 0000  // Zero - Value Palyer bit is not set

【讨论】:

    【解决方案3】:

    & 是按位运算符 AND。如果两个位(运算符的左操作数和右操作数)也等于 1,则其应用的结果等于 1。否则结果为 0。

    例如,假设sf::Time dt 的值AlliedAircraft 定义为AlliedAircraft = 1 &lt;&lt; 2,相当于二进制文字0b100。所以如果command.category 也包含AlliedAircraft 那么运算符& 的结果将是相同的值0b100

    0b100
    &
    0b100
    =====
    0b100
    

    所以0b100不等于0,那么if语句中的条件就等于true

    假设command.category 包含例如PlayerAircraft 定义为PlayerAircraft = 1 &lt;&lt; 1 又相当于二进制文字0b010 在这种情况下,我们将得到

    0b010
    &
    0b100
    =====
    0b000
    

    结果值将等于 0,对应的条件将等于false

    【讨论】:

      【解决方案4】:

      1 &lt;&lt; 000000001 二进制)

      1 &lt;&lt; 100000010 二进制)

      1 &lt;&lt; 200000100 二进制)

      所以如果我写: PlayerAircraft = 2

      2 是二进制的00000010,所以2 &lt;&lt; 14(二进制的00000100)。

      【讨论】:

        猜你喜欢
        • 2015-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多