【问题标题】:Zero-initializing an array data member in a constructor在构造函数中对数组数据成员进行零初始化
【发布时间】:2014-06-02 04:49:36
【问题描述】:

我有一个类对象数组,在类对象内部我有另一个数组,我需要将其初始化为全零。代码编译并运行,但我的输出显示 C 而不是 0。

来自头文件:

class Cache {
private:
    int byte[16];
public:
    Cache();
    int getBytes(int);
    ~Cache();
};

来自 cpp 文件

Cache::Cache()  
{
    byte[16]={0};
}

int Cache::getBytes(int j){
    return byte[j];
}

来自另一个 cpp 文件

for (int i = 0; i < 16; i++) 
{
    for (int j = 0; j < 16; j++)  //visual check of initializes main memory
    {
        cout << cache[i].getBytes(j) << " ";
}
}

设置正确吗?正如我所提到的,getBytes 正在返回 'C' 而不是 '0' 如预期的那样。

【问题讨论】:

  • 仅供参考:C++11 允许您在构造函数初始值设定项列表上进行值初始化:例如,Cache::Cache() : byte() {}。这将对每个元素进行值初始化,这对于标量意味着零初始化,这正是您想要的。
  • @WhozCraig 这也是在 C++03 中实现的方式。我添加了一个答案,但我确定我之前已经这样做过,所以我正在寻找副本。
  • @juanchopanza 我知道至少有一些(我创作了一些,我相信你也写了一些=P)。然而,我终其一生都找不到它们。不知道那是03x的事情。我从来没有真正坚持中等规格,而是等到我们终于拿到了 11 个。
  • @WhozCraig 我不会将 03 视为中间规格。我会说 98 从未真正发生过 :-)
  • @juanchopanza 哈哈。我不认为只有你一个人想假装是这种情况=P

标签: c++ arrays constructor


【解决方案1】:

只需在构造函数初始化列表中使用值初始化。这是在 C++ 中执行此操作的惯用方式。

Cache::Cache() : byte()
{ 
}

请注意,C++11 也允许使用这种语法:

Cache::Cache() : byte{}
{ 
}

如果您想知道为什么这适用于 C++ 11 标准(注意这也适用于 C++03):

C++11 § 8.5,p10

初始化器为空括号集的对象,即(),应值初始化

value-initialized这个词带我们去:

C++11 § 8.5,p7

对 T 类型的对象进行值初始化意味着:

  • 如果 T 是具有用户提供的构造函数 (12.1) 的(可能是 cv 限定的)类类型9,则调用 T 的默认构造函数(并且如果T 没有可访问的默认构造函数);

  • 如果 T 是没有用户提供的构造函数的(可能是 cv 限定的)非联合类类型,则该对象被零初始化,并且如果 T 的隐式声明的默认构造函数不平凡,则调用该构造函数.

  • 如果T是一个数组类型,那么每个元素都是值初始化的;

  • 否则,对象被零初始化。

这里的第三个选项触发每个元素的值初始化;第四种适用于我们到达这些元素中的每一个,因为它们 (a) 没有类类型,因此 (1) 和 (2) 消失了,并且 (b) 不是数组,因此 (3) 消失了。只剩下最后一个,并且你的元素是零初始化的。

【讨论】:

    【解决方案2】:

    Cache 构造函数中,当你这样做时:

    byte[16]={0};
    

    您仅设置数组的第 16 个字节(超出范围,因此此操作具有未定义的行为)。数组对象在 C++ 中是默认初始化的,因为你存储了int,所以不会执行初始化。

    你可以使用std::fill来初始化它:

    Cache::Cache()  
    {
      std::fill(byte, byte+16, 0);
    }
    

    或者您可以在数组上使用常规 for 循环。

    【讨论】:

    • 这就解决了。感谢您的帮助!
    【解决方案3】:

    你在很多层面上都做错了。您使用的语法并不像您认为的那样。您现在所做的实际上是将表格的第 17 个元素初始化为 0。

    在您的情况下,memset 可能是最快和最简单的。但是,它不适用于复杂类型,因此我会考虑为一般情况编写一个简单的 sn-p,例如:

    template<typename T>
    inline void zero_init(T array[], size_t elements){
     if( std::is_pod<T>() ) memset(array, 0, sizeof(T)*elements);
     else std::fill(begin(array), begin(array)+elements, 0);
    }
    

    这将检查类型是否为 POD 类型,在这种情况下,这意味着它可以通过 memset 进行初始化,并将 0 用于整个表。如果T 不支持它,那么对于每个元素,将调用一个等价的element = 0。也可以在编译时评估检查,因此很可能 if 将被编译掉,并在编译时为每种类型创建一个简单的“单行”版本。

    您可以通过以下方式调用它:

    Cache::Cache()  
    {
      zero_init(byte, 16);
    }
    

    【讨论】:

    • 最快最简单的方法是值初始化(见我的回答)
    • 是的,对于零初始化,它将在编译时完成。在其他情况下使用它可能没有意义(作为其他东西的模板)。但我想我会留下它,尽管你的回答使它变得毫无用处。好吧,太糟糕了,我脑子里没有标准来想出这么好的答案。 好嫉妒。 =)
    【解决方案4】:

    你的代码有两个问题:

    byte[16]={0};
    

    数组具有基于0 的索引,因此这种情况下的最大索引可以是15,而不是16。你正在破坏内存。

    其次,您必须遍历所有元素并对其进行初始化。您的初始化方式将仅针对一个元素完成。

    Cache::Cache()  
    {
      for(int i=0;i<16;i++)
       byte[i]=0;
    }
    

    【讨论】:

      【解决方案5】:

      memset 是最简单的解决方案。

      Cache::Cache()  
      {
        memset(byte, 0, sizeof(byte));
      }
      

      【讨论】:

      • std::fill 对于 char 类型与 memset 一样工作,但对于其他类型也能正常工作,因此它可能是一个更好的选择(并且不太容易以错误的顺序获取参数!)
      【解决方案6】:

      C++11 支持这种语法:

      #include <iostream>
      
      using namespace std;
      
      class Test
      {
      private:
         int bytes[16] = {};
         
      public:
         void print()
         {
            for(int i = 0; i < 16; i++)
            {
               cout << bytes[i] << " ";
            }
            cout << endl;
         }
      };
      
      int main()
      {
         Test test;
         test.print();
      }
      

      基本上使用zero initializationdefault member initializer(默认构造函数)

      【讨论】:

        猜你喜欢
        • 2011-05-02
        • 2021-12-26
        • 1970-01-01
        • 2021-03-26
        • 2020-09-30
        • 1970-01-01
        • 1970-01-01
        • 2017-06-23
        相关资源
        最近更新 更多