【问题标题】:What is the best way to initialize a bitfield struct in C++?在 C++ 中初始化位域结构的最佳方法是什么?
【发布时间】:2009-03-04 20:51:36
【问题描述】:

在 C++ 中,我有一个包含匿名位域结构的类。我想将其初始化为零,而不必手动写出所有字段。

我可以想象将初始化放在三个地方:

  1. 在位域中创建构造函数
  2. 在包含类的构造函数的初始化列表中清零
  3. 在包含类的构造函数主体中清零

这个位域有很多字段,我不想一一列举。

例如看下面的代码:

class Big {
    public:
        Big();

        // Bitfield struct
        struct bflag_struct {
            unsigned int field1 : 1;
            unsigned int field2 : 2;
            unsigned int field3 : 1;
            // ...
            unsigned int field20 : 1;
            // bflag_struct(); <--- Here?
        } bflag;

        unsigned int integer_member;
        Big         *pointer_member;
}

Big::Big()
  : bflag(),             // <--- Can I zero bflag here?
    integer_member(0),
    pointer_member(NULL)
{
    // Or here?
}

其中之一更可取吗?还是我还缺少其他东西?

编辑:根据下面接受的答案(由 Ferruccio),我决定采用此解决方案:

class Big {
    // ...

    struct bflag_struct {
        unsigned int field 1 : 1;
        // ...
        bflag_struct() { memset(this, 0, sizeof *this); };
    }

    // ...
}

【问题讨论】:

    标签: c++ constructor struct initialization bit-fields


    【解决方案1】:

    你总是可以在你的构造函数中这样做:

    memset(&bflag, 0, sizeof bflag);
    

    【讨论】:

      【解决方案2】:

      将位域结构与更容易初始化为 0 的东西联合起来。

      【讨论】:

        【解决方案3】:

        您可以使用联合,尽管这会在访问字段时增加额外的间接级别:

        class Big {
            union {
                struct {
                    unsigned int field1 : 1;
                    ...
                } fields;
                unsigned int all_fields;
            };
            ...
        };
        
        Big::Big()
          : all_fields(0),
            ...
        {
            ...
        }
        

        MSVC 允许在联合内部使用匿名结构(例如,请参阅 &lt;d3d9.h&gt; 中的 D3DMATRIX 的定义),但这是一个非标准 C++ 扩展,您应该尽可能避免使用它。

        【讨论】:

          【解决方案4】:

          您使用类似函数的初始化程序(标记为“我可以在此处将 bflag 归零吗?”)100% 足以用 0 值初始化您的 POD 结构。

          除非您知道您的编译器在这方面有问题,否则对这些成员进行任何额外的初始化都会将其初始化两次,但没有任何好处。

          编辑:只是为了“好玩”,我刚刚用 VS2005、VS2008、GCC 3.4.4、GCC 4.2 和 Borland C++ 5.5.1 进行了检查……只有 Borland C++ 5.5.1 弄错了。

          我说“错误”是因为在我看来,标准的 8.5 和 8.5.1 暗示类似函数的初始化程序应该零初始化 POD 结构。

          【讨论】:

          • 恐怕我需要非常确保出于安全关键原因将数据归零,并且我们在多个平台上进行编译。感谢您的测试。
          【解决方案5】:

          BTW C++20 支持在类定义中初始化位域,例如

          class ... {
             int foo : 1 {};
          }
          

          使用 -std=c++2a 启用 gcc

          【讨论】:

            【解决方案6】:

            顺便说一句,除非您需要位域来连接一些遗留代码,否则您不应该使用它们。它们本质上是不可移植且效率低下的。

            【讨论】:

            • 这是我正在从 calloc 转换为新代码的遗留代码。位域可以出现数十万次,因此内存至关重要。
            • 我认为它是不可移植的(从某种意义上说,不同的架构可能会将比特存储在不同的位置),但它的效率如何?
            • 我相信处理器必须做更多的工作来检索位字段中的位,而不是使用布尔值。对于我们的应用程序,为了节省内存而降低速度是值得的。
            • 位域是完全可移植的。如果您开始假设编译器将如何分配位,那么您将引入未定义的行为。此外,位域的全部意义在于告诉编译器,在这种特殊情况下,空间效率比时间效率更重要。
            • 明确地说,只有当您直接将内存序列化到磁盘并期望另一台计算机(可能运行不同的平台!)将其反序列化为完全相同的表示时,它才是不可移植的。但是你的序列化不应该是内存转储,所以......
            【解决方案7】:

            您可以在构造函数中使用 ZeroMemory 或 memset 将内存归零,这样看起来更干净。

            【讨论】:

              猜你喜欢
              • 2010-09-17
              • 2011-05-10
              • 1970-01-01
              • 2017-11-01
              • 1970-01-01
              • 2012-01-28
              • 2016-02-22
              • 2010-10-13
              • 2016-02-09
              相关资源
              最近更新 更多