当我用它的输出检查下面的程序时,我发现通过返回值获取FrameA对象非常困惑:
当让编译器生成ctor时,成员数组字段初始化为全0
auto a = f(); // f() --> return A();
考虑以下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-initialize
和default-initialize
似乎并没有涵盖这种情况。我错过了什么吗?
答案 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
的定义方式:
T
没有默认构造函数或者有默认的默认构造函数,则编译器负责初始化:编译器值初始化所有成员。对于内置类型值初始化意味着它是零初始化,即值接收它们相应的合适的零表示。T
具有非默认的默认构造函数,T
的程序员将负责初始化成员。成员要么在成员初始化列表中列出并相应地初始化,要么默认初始化。内置类型的默认初始化意味着没有任何反应,即这些成员未初始化。答案 2 :(得分:1)
A()
使用值初始化。正如您所注意到的,这取决于A
是否具有用户声明的默认构造函数。