#include <string>
struct T1 {
int _mem1;
int _mem2;
T1() = default;
T1(int mem2) : T1() { _mem2 = mem2; }
};
T1 getT1() { return T1(); }
T1 getT1(int mem2) { return T1(mem2); }
int main() {
volatile T1 a = T1();
std::printf("a._mem1=%d a._mem2=%d\n", a._mem1, a._mem2);
volatile T1 b = T1(1);
std::printf("b._mem1=%d b._mem2=%d\n", b._mem1, b._mem2);
// Temporarily disable
if (false) {
volatile T1 c = getT1();
std::printf("c._mem1=%d c._mem2=%d\n", c._mem1, c._mem2);
volatile T1 d = getT1(1);
std::printf("d._mem1=%d d._mem2=%d\n", d._mem1, d._mem2);
}
}
当我使用gcc5.4进行编译时,得到以下输出:
g++ -std=c++11 -O3 test.cpp -o test && ./test
a._mem1=0 a._mem2=0
b._mem1=382685824 b._mem2=1
为什么b
的委派给默认构造函数的用户定义构造函数无法将_mem1设置为零,而使用默认构造函数的a
却被初始化为零?
Valgrind也确认了这一点:
==12579== Conditional jump or move depends on uninitialised value(s)
==12579== at 0x4E87CE2: vfprintf (vfprintf.c:1631)
==12579== by 0x4E8F898: printf (printf.c:33)
==12579== by 0x4005F3: main (in test)
如果我将if(false)更改为if(true)
然后输出与您期望的一样
a._mem1=0 a._mem2=0
b._mem1=0 b._mem2=1
c._mem1=0 c._mem2=0
d._mem1=0 d._mem2=1
编译器在做什么?
答案 0 :(得分:2)
简短答案:对于琐碎的类型,两种不同形式的“默认构造”会导致两种不同的初始化:
T a;
,在这种情况下,对象是默认初始化的。其值不确定,未定义的行为将很快发生(这是初始化b.mem1
的方式以及valgrind检测错误的原因。)T a=T();
,在这种情况下,对象被值初始化,并且其整个内存被清零(a.mem1
和a.mem2
会发生这种情况) 长答案:实际上,T1
的默认构造函数不是a.mem1初始化为零的原因。 a
首先进行了零初始化,但未进行b
初始化,因为该标准的单一规则不适用于b
的初始化程序。
定义volatile a=T()
导致a
为value-initialized (1)。 struct T1
作为否user-provided的默认构造函数(2)。对于这种结构,整个对象是zero-initialized,如C ++ 11标准[dcl.init]/7.2的此规则所述:
如果T是(可能是cv限定的)非工会类类型,而没有用户提供的构造函数,则该对象将初始化为零,并且,如果T的隐式声明的默认构造函数不是平凡的,则将该构造函数称为
C ++ 11和C ++ 17之间存在细微的差异,导致定义volatile b=T(1)
在C ++ 11中是未定义行为,但在C ++ 17中不是。在C ++ 11中,通过复制对象类型b
初始化T1
,该对象类型由表达式T(1)
初始化。此副本构造评估T(1).mem1
,这是一个未确定的值。这是forbidden。在c ++ 17中,b
由 prvalue 表达式T(1)
直接初始化。
printf
内部对此不确定值的评估也是未定义行为,独立于c ++标准。这就是valgrind抱怨的原因,并且当您将if (true)
更改为if (false)
时会看到不一致的输出。
(1)严格来说,a
是从c ++ 11中的值初始化对象构造而成的副本
(2)T1的默认构造函数不是用户提供的,因为它在第一个声明中被定义为默认构造
答案 1 :(得分:0)
简短答案
您代码中的default
构造函数被认为是琐碎的,并且这种构造函数不执行任何操作,即保持事物统一。
更长的答案
临时默认构造函数
类T的默认构造函数很简单(即不执行 操作)是否满足以下所有条件:
The constructor is not user-provided (i.e., is implicitly-defined or defaulted on its first declaration) T has no virtual member functions T has no virtual base classes T has no non-static members with default initializers.
(自C ++ 11起)
Every direct base of T has a trivial default constructor Every non-static member of class type has a trivial default constructor
>普通的默认构造函数是不执行任何操作的构造函数 操作。。所有与C语言兼容的数据类型(POD类型)都是 可以默认构造的。但是,与C语言不同,带有 简单的默认构造函数不能简单地创建 重新解释适当对齐的存储,例如分配给 std :: malloc:正式引入新的位置需要放置新的 反对并避免潜在的不确定行为。