Флаги, перечисление (C)
Я не очень привык к программированию с флагами, но я думаю, что я нашел ситуацию, в которой они были бы полезны:
У меня есть несколько объектов, которые регистрируются как слушатели определенных событий. Какие события они регистрируют, зависит от переменной, которая отправляется им при их создании. Я думаю, что хороший способ сделать это - отправить побитовые ИЛИ связанные переменные, такие как: TAKES_DAMAGE | GRABBABLE | LIQUID и т.д. Затем в конструкторе объект может проверить, какие флаги установлены, и зарегистрировать его как слушателя для тех, которые есть.
Но здесь я запутался. Предпочтительно, флаги будут в перечислении. Но это тоже проблема. Если у нас есть эти флаги:
enum
{
TAKES_DAMAGE,/* (0) */
GRABBABLE, /* (1) */
LIQUID, /* (2) */
SOME_OTHER /* (3) */
};
Затем отправка флага SOME_OTHER (3) будет таким же, как отправка GRABBABLE | ЖИДКОСТЬ, не так ли?
Как именно вы имеете дело с этим материалом?
Ответы
Ответ 1
Ваше перечисление должно быть двух:
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
Итак, скажем, вы установили 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
Ответ 2
другой способ хранения флагов - вообще не беспокоиться о базовом типе. при использовании перечисления значения перечисления сохраняются по умолчанию в unsigned 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();
этот метод позволяет вам определять любое количество флагов без ограничений, и вы можете в любое время расширить свой флаг, не опасаясь переполнения. недостатком является то, что тестирование подмножества флагов является более громоздким и требует больше кода.
Ответ 3
Вы должны сделать флаги только степенями по два, т.е. каждый бит в любом типе данных, который вы храните в этом, и ничего не перекрывается, когда вы побитовое ИЛИ.
Ответ 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)
};
Ответ 6
вам нужно
enum
{
TAKES_DAMAGE = 1,
GRABBABLE = 2,
LIQUID = 4,
SOME_OTHER = 8
};