【问题标题】:Enum bitfield container class枚举位域容器类
【发布时间】:2009-02-07 16:09:48
【问题描述】:

我正在尝试编写一个小类来更好地理解 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: 

【问题讨论】:

    标签: c++ enums bit-fields bitflags


    【解决方案1】:

    我个人会使用 std::vector 来处理标志,因为它是一种将布尔值打包成位的特化。

    但是:

    我认为你的删除标志有点复杂,试试这个

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

    编辑: 有评论问我为什么不do m_flags &amp;= ~f。我把这个问题当作“学习者”问题而不是优化问题。我展示了如何使他的代码正确,而不是快速。

    【讨论】:

    • 如果没有事先检查,擦除标志很可能更快(也更简单)。
    • 你是对的。我认为这是 OP 的学习经历,并简单地纠正了他的代码以使其正常工作。我没有更正他的代码以使其更快。
    • 如果 f 设置了超过 1 位,但并非所有位都设置在 flag 中怎么办? flag&=~f 解决了这个问题。
    • 标志是一个枚举并且不能有多个值。
    • 如果 std::vector 有特化,我怎么看到这么多位域还在使用?
    【解决方案2】:

    您的 remove_flag() 方法中有一个错误,它应该是 flags |= i;

    但是,像这样 O(1) 做到这一点:

    void remove_flag(flag f) { m_flags &= ~f; }
    

    【讨论】:

      【解决方案3】:

      has_flag()remove_flag() 是错误的。他们应该是这样的:

      bool has_flag(flag f) { return !!(m_flags & f); }
      void remove_flag(flag f) 
      {
          m_flags &= ~f;
      }
      

      【讨论】:

        【解决方案4】:

        每个人都已经掌握了这个:flag &= ~f;

        You might look at my earlier posting.

        has_flag():如果 f 中的所有位都已设置,您是否希望返回 true?或者如果至少设置了其中一个?这是 flags&f==f 和 flags&f!=0 之间的区别。

        你可以考虑#include 和 cout

        枚举可以在类 Holder 中。

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

        然后您将使用 Holder::A 而不是 A

        你可能想使用 for(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 美元。

        【讨论】:

          猜你喜欢
          • 2023-03-07
          • 1970-01-01
          • 1970-01-01
          • 2016-02-08
          • 2018-09-20
          • 2011-07-17
          • 1970-01-01
          • 2020-12-16
          • 1970-01-01
          相关资源
          最近更新 更多