default构造函数是否初始化成员数组变量?

时间:2013-12-17 16:04:59

标签: c++ arrays

当我用它的输出检查下面的程序时,我发现通过返回值获取FrameA对象非常困惑:

  1. 定义一个空ctor时,成员数组字段保持未初始化
  2. 让编译器生成ctor时,成员数组字段初始化为全0

    auto a = f();       // f() --> return A();
    
  3. 考虑以下SSCCE

    #include <cstring>
    #include <iostream>
    #include <chrono>
    #include <algorithm>
    
    using namespace std;
    
    const int MAX = 9999999;
    
    struct FrameA {
      // FrameA() {}
      // FrameA(const FrameA &v) { memcpy(data, v.data, sizeof(data)); }
      char data[1000];
    };
    
    FrameA f(int i) { return FrameA(); }
    
    int test(int odd) {
      int sum = 0;
      auto begin = chrono::steady_clock::now();
      for (int i = 0; i < MAX; ++i) {
        auto v = f(odd);
        sum += v.data[0] + v.data[330];
      }
      auto end = chrono::steady_clock::now();
      cout << chrono::duration_cast<chrono::milliseconds>(end - begin).count()
           << " (milliseconds)" << endl;
      return sum;
    }
    
    int _tmain(int argc, _TCHAR *argv[]) {
      test(0);
      test(1);
      return 0;
    }
    

    定义空ctor时,输出如下:

      

    g ++ v4.8.1

         

    72(毫秒)
      73(毫秒)

    但是使用编译器生成的ctor,输出是:

      

    g ++ v4.8.1

         

    1401(毫秒)
      1403(毫秒)

    我也在VC12上测试过,结果很相似。

    检查程序集后,我发现使用编译器生成的ctor:

      for (int i = 0; i < MAX; ++i) {
        auto v = f(odd);
    00A31701  push        3E8h  
    00A31706  lea         eax,[ebp-3F8h]  
    00A3170C  push        0  
    00A3170E  push        eax  
    00A3170F  call        _memset (0A32460h)               ;; memset FrameA to 0
        sum += v.data[0] + v.data[330];
    00A31714  movsx       eax,byte ptr [ebp-3F8h] 
    

    但是使用空的ctor不会调用memset将FrameA中的数组设置为零。

    对此有任何解释吗?

    顺便说一下,我搜索了C ++ 11草案n3242,但是第8.5章zero-initializedefault-initialize似乎并没有涵盖这种情况。我错过了什么吗?

3 个答案:

答案 0 :(得分:3)

FrameA()将对对象进行值初始化(第5.2.3 / 2节):

  

表达式T(),其中T简单类型说明符 typename-specifier ,用于非数组完整对象类型或者(可能是cv限定的)void类型,创建一个指定类型的prvalue,它是值初始化的

初始化没有用户提供的构造函数的非联合类类型的值将对其进行零初始化(第8.5 / 7节):

  

如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T隐式声明的默认值构造函数是非平凡的,该构造函数被调用。

这会对每个成员进行零初始化。

初始化具有用户提供的构造函数的类类型将简单地调用构造函数(在您的情况下,不会初始化数组)(§8.5/ 7):

  

如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(并且初始化为如果T没有可访问的默认构造函数,则格式错误;

答案 1 :(得分:3)

使用默认构造函数构造类型为T的对象,即使用T()得到值初始化默认构造取决于T的定义方式:

  1. 如果T没有默认构造函数或者有默认的默认构造函数,则编译器负责初始化:编译器值初始化所有成员。对于内置类型值初始化意味着它是零初始化,即值接收它们相应的合适的零表示。
  2. 如果T具有非默认的默认构造函数,T的程序员将负责初始化成员。成员要么在成员初始化列表中列出并相应地初始化,要么默认初始化。内置类型的默认初始化意味着没有任何反应,即这些成员未初始化。

答案 2 :(得分:1)

A()使用值初始化。正如您所注意到的,这取决于A是否具有用户声明的默认构造函数。

  • 如果是,则调用。未由构造函数显式初始化的任何成员将保持未初始化状态。对于像这样的自动或临时对象,这意味着它们不会被触及,并且将包含发生在内存中的任何垃圾。
  • 如果没有,则每个成员都将初始化值。对于大多数基本类型,值初始化将它们设置为零。