Enum битовый класс контейнера - PullRequest
1 голос
/ 07 февраля 2009

Я пытаюсь написать небольшой класс, чтобы лучше понять битовые флаги в 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: 

Ответы [ 4 ]

3 голосов
/ 07 февраля 2009

has_flag () и remove_flag () неверны. Они должны идти так:

bool has_flag(flag f) { return !!(m_flags & f); }
void remove_flag(flag f) 
{
    m_flags &= ~f;
}
3 голосов
/ 07 февраля 2009

лично я бы использовал std :: vector для обработки флагов, так как это специализация, которая упаковывает bools в бит.

Тем не менее:

Я думаю, ваш флаг удаления немного сложен, попробуйте вместо этого

void remove_flag( flag f ) 
{
   if ( has_flag( f ) == true )
   {
      m_flags ^= f;   // toggle the bit leaving all other unchanged
   } 
}

Edit: Комментарий спросил, почему я просто не do m_flags &= ~f. Я воспринял вопрос как вопрос «ученика», а не как вопрос оптимизации. Я показываю, как сделать его код правильным, а не быстрым.

3 голосов
/ 07 февраля 2009

В вашем методе remove_flag () есть ошибка, это должны быть flags | = i;

Но, сделай это O (1) так:

void remove_flag(flag f) { m_flags &= ~f; }
0 голосов
/ 07 февраля 2009

Все уже прибили это: flag & = ~ f;

Вы можете посмотреть на мою предыдущую публикацию.

has_flag (): вы хотите вернуть true, если все биты в f установлены? Или если хотя бы один из них установлен? Это разница между флагами & f == f против флагов & f! = 0.

Вы можете рассмотреть #include и cout

Перечисление может быть внутри класса Holder.

class Holder
{
public:
  enum flag { A=1, B=2, C=4; };
...
};

Затем вы бы использовали Держатель :: A вместо A .

Возможно, вы захотите использовать для (i = 0; i

Возможно, вы захотите, чтобы ваши методы add_flag / has_flag / remove_flag принимали int, а не перечислимый тип. Это избавляет от большого количества кастинга. Если вы не хотите поддерживать все возможные значения int, можно использовать метод проверки и путь отклонения. Кстати, ничто не мешает мне вызвать add_flag (flag (5736)). И вы уже довольно часто используете enum_flag.

Вы можете использовать mFlag, а не m_flag. Это твой выбор. Но когда вы смотрите на код, подобный m_x * m_y-m_z * m_y-m_x * m_z, в зависимости от вашего шрифта, можно легко принять _ за - (Или наоборот.)

Аналогично, рассмотрите addFlag, а не add_flag. Для чего-то подобного это не имеет значения. Но когда у вас длинное описательное имя, эти подчеркивания начинают складываться, используя пространство вверх. Соблазн состоит в том, чтобы сокращать имя, делая ваш код более тупым.

Только мои 0,02 доллара.

...