Класс контейнера битового поля Enum
-
22-08-2019 - |
Вопрос
Я пытаюсь написать небольшой класс, чтобы лучше понимать битовые флаги в c ++.Но что-то не получается.Он выводит неправильные значения.В чем заключается проблема?Я неправильно понял, как добавлять флаги?Или проверить, есть ли они в битовом поле?
Вот код:
#include <iostream>
enum flag
{
A = 1, B = 2, C = 4
};
class Holder
{
public:
Holder() : m_flags(A) {}
~Holder() {}
void add_flag(flag f) { m_flags |= f; }
bool has_flag(flag f) { return ((m_flags&f)==f); }
void remove_flag(flag f)
{
unsigned int flags = 0;
for (int i = 1; i<=(int)C; i *= 2)
{
if ((flag)i!=f && has_flag(f))
flags |= f;
}
m_flags = flags;
}
void print()
{
std::cout << "flags are now: " << m_flags << " | holding: ";
for (int i = 1; i<=(int)C; i *= 2)
{
if (has_flag((flag)i))
std::cout << i << " ";
}
std::cout << std::endl;
}
private:
unsigned int m_flags;
};
int main()
{
Holder h;
h.print(); // should print 1
h.add_flag(B);
h.print(); // should print 1 2
h.remove_flag(A);
h.print(); // should print 2
h.add_flag(C);
h.print(); // should print 2 4
h.remove_flag(B);
h.print(); // should print 4
}
Вывод программы:
flags are now: 1 | holding: 1
flags are now: 3 | holding: 1 2
flags are now: 1 | holding: 1
flags are now: 5 | holding: 1 4
flags are now: 0 | holding:
Решение
лично я бы использовал std::vector< bool > для обработки флагов, поскольку это специализация, которая упаковывает bools в bit.
Однако:
Я думаю, что ваш флаг удаления немного сложен, попробуйте это вместо
void remove_flag( flag f )
{
if ( has_flag( f ) == true )
{
m_flags ^= f; // toggle the bit leaving all other unchanged
}
}
Редактировать:В комментарии спрашивалось, почему я просто не do m_flags &= ~f
.Я воспринял этот вопрос как вопрос "ученика", а не как вопрос оптимизации.Я показываю, как сделать его код правильным, а не быстрым.
Другие советы
В вашем методе remove_flag() есть ошибка, это должны быть flags |= i;
Но сделайте это O (1) следующим образом:
void remove_flag(flag f) { m_flags &= ~f; }
has_flag() has_flag() и remove_flag() удалить флаг() ошибаются.Они должны идти вот так:
bool has_flag(flag f) { return !!(m_flags & f); }
void remove_flag(flag f)
{
m_flags &= ~f;
}
Все уже поняли это:флаг &= ~f;
Вы могли бы взглянуть на мое предыдущее сообщение.
has_flag() has_flag():Вы хотите вернуть true, если установлены все биты в f?Или если хотя бы один из них установлен?Это разница между flags&f==f против flags&f !=0.
Вы могли бы рассмотреть #включить <iomanip> и cout << шестнадцатеричный <(Преобразование шестнадцатеричного числа в разрядное легче выполнить в вашей голове.)
Перечисление может находиться внутри держателя класса.
class Holder
{
public:
enum flag { A=1, B=2, C=4; };
...
};
Затем вы бы использовали Держатель::A вместо того , чтобы A.
Возможно, вы захотите использовать for(i=0;i
Возможно, вы захотите, чтобы ваши методы add_flag / has_flag /remove_flag принимали значение int, а не перечисляемый тип.Это избавляет от большого количества кастингов.Если вы не хотите поддерживать все возможные значения int, можно использовать метод проверки и путь отклонения.Кстати, ничто не мешает мне вызвать add_flag(флаг (5736)).И вы уже довольно часто применяете приведение к enum_flag .
Возможно, вы захотите использовать mFlag, а не m_flag.Это твой выбор.Но когда вы смотрите на код типа m_x * m_y-m_z * m_y-m_x * m_z , в зависимости от вашего шрифта, _ может быть легко ошибочно принято за -.(Или наоборот.)
Аналогично, рассмотрите addFlag, а не add_flag .Для чего-то подобного это не имеет значения.Но когда у вас длинное описательное имя, эти подчеркивания начинают складываться, расходуя пространство между строками.Тогда возникает соблазн сократить название, сделав ваш код более запутанным.
Только мои 0,02 доллара.