Удобным решением для получения различного поведения от некоторой функции является использование битовых флагов, которые передаются ей в качестве входных параметров. Давайте разберемся, когда уместно применять и в чем заключается этот прием.
Постановка задачи
Предположим, нам нужна функция для управления светодиодными индикаторами клавиатуры (Scroll Lock, Num Lock и Caps Lock). Чтобы не усложнять приложение, реализуем его схематично. Будем выводить на консоль состояние, соответствующее переданным битовых флагам. Для этого используем следующую функцию:
1 2 3 4 5 6 7 8 |
void printLEDState( bool on ) { if( on ) { std::cout << " * "; } else { std::cout << " _ "; } } |
Если индикатор включен, то на консоли отобразится символ *, иначе _.
Применение битовых флагов
Первым делом определим следующее перечисление:
1 2 3 4 5 6 7 8 |
enum KeyboardLED { NONE = 0, // 0000 NUM_LOCK = 1, // 0001 CAPS_LOCK = 1 << 1, // 0010 SCROLL_LOCK = 1 << 2, // 0100 ALL = NUM_LOCK | CAPS_LOCK | SCROLL_LOCK // 0111 }; |
Рядом с каждым значением в виде комментария мы указали его двоичное представление. Обратите внимание, что значения NUM_LOCK
, CAPS_LOCK
и SCROLL_LOCK
выбраны таким образом, что единицы в их бинарном представлении не накладываются. Для определения этих значений мы использовали оператор бинарного сдвига <<
на один бит влево. Это означает, что бинарное умножение любого из них даст в результате ноль, то есть они в совокупности образуют набор битовых флагов.
Еще один интересный прием, который мы здесь наблюдаем, заключается в использовании комбинированного флага ALL
. Он представляет собой побитовое сложение |
всех возможных битовых флагов.
Реализуем саму функцию для установки состояния индикаторов:
1 2 3 4 5 6 7 |
void setKeyboardLEDState( unsigned int state ) { printLEDState( state & NUM_LOCK ); printLEDState( state & CAPS_LOCK ); printLEDState( state & SCROLL_LOCK ); std::cout << std::endl; } |
На вход она принимает параметр state
. Проверка того, должен ли быть включен каждый индикатор или нет, осуществляется с помощью оператора побитового умножения &
. Например, для CAPS_LOCK
мы видим:
1 |
state & CAPS_LOCK |
Если биты в state
и CAPS_LOCK
наложатся, то в функцию printLEDState()
будет передана истина, иначе ложь.
Рассмотрим пример, чтобы лучше понять, как это работает. Передадим функции setKeyboardLEDState()
параметр state
, равный NUM_LOCK | CAPS_LOCK
(то есть 0001 | 0010 = 0011
). Во время проверок state & NUM_LOCK
(0011 & 0001 = 0001
) и state & CAPS_LOCK
(0011 & 0010 = 0010
) мы видим, что единицы в бинарном представлении накладываются и получаются ненулевые значения, которые соответствуют true
. Однако в последней проверке state & SCROLL_LOCK
(0011 & 0100 = 0000
) получается ноль, который соответствует false
. В результате на экран будет выведено: * * _
.
А вот еще несколько примеров запуска с указанием результатов, которые будут выведены на консоль:
1 2 3 4 |
setKeyboardLEDState( NONE ); // _ _ _ setKeyboardLEDState( NUM_LOCK | CAPS_LOCK ); // * * _ setKeyboardLEDState( ALL ); // * * * |
Заключение
Использование битовых флагов — довольно удобный и одновременно мощный прием. Его сильной чертой является возможность комбинирования различных режимов работы.