请考虑以下代码:
class A {
public:
int i;
A() {}
};
class B {
public:
A a;
int i;
};
int main() {
B* p = new B {};
std::cout << p->i << " " << p->a.i << "\n";
}
在clang ++中使用-std = c ++ 11编译,p->i
结果为零,但p->a.i
没有。只要它的类没有用户提供的构造函数,整个对象是否应该归零?
对
T
类型的对象进行值初始化意味着:
- 如果
T
是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(并且如果初始化是错误的,则T
没有可访问的默认构造函数);- 如果
T
是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T
隐式声明的默认值构造函数是非平凡的,该构造函数被调用。- 如果
T
是数组类型,则每个元素都是值初始化的;- 否则,该对象为零初始化。
零初始化T类型的对象或引用意味着:
- 如果
T
是标量类型(3.9),则将对象设置为值0
(零),作为整数常量表达式,转换为T
;- 如果
T
是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位;- 如果
T
是一个(可能是cv限定的)联合类型,则该对象的第一个非静态命名数据成员被零初始化,并且填充被初始化为零位;- 如果
T
是数组类型,则每个元素都是零初始化的;- 如果
T
是引用类型,则不执行初始化。
每个的第二个子弹都适用于此。
答案 0 :(得分:13)
根据C ++ 11标准加相关DR
,Clang是正确的在最初的C ++ 11规范中,B{}
将执行值初始化,导致a.i
被零初始化。对于像
B b = {};
...在C ++ 98中作为聚合初始化处理,但在C ++ 11 FDIS中被视为值初始化。
但是,此情况下的行为由core issue 1301更改,它通过强制在 braced-init-list <初始化聚合时使用聚合初始化来恢复C ++ 98行为/ em>的。由于此问题被视为DR,因此它被视为事实上应用于C ++标准的早期版本,因此符合要求的C ++ 11编译器将在此处执行聚合初始化而不是值 - 初始化。
最终,依靠值初始化来初始化数据成员是一个坏主意,尤其是对于具有用户提供的构造函数的类。
答案 1 :(得分:6)
它确实看起来像一个错误(或者,正如评论中指出的那样,尽管指定了C ++ 11,但仍按照C ++ 03行事)。在C ++ 11中,值初始化应该在调用其默认构造函数之前将a
的成员归零。 B
的初始化受此8.5 / 7
如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么对象是零初始化,如果T的隐式声明的默认构造函数是非-trivial,调用该构造函数。
零初始化应该按照8.5 / 5的规则递归零初始化a
如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化
当然,a
的零初始化应将i
设置为零。
答案 2 :(得分:5)
不编译器错误,这是代码中的错误。编译器似乎正在实现C ++ 03行为,但这在C ++ 11中发生了重大变化。
这些是来自C ++ 03和C ++ 11标准的一些相关引用
在C ++ 03中:
对T类型的对象进行值初始化意味着:
- 如果T是类类型 (第9节)用户声明的构造函数(12.1),然后是默认值 调用T的构造函数(如果T,初始化是错误的 没有可访问的默认构造函数);
- 如果T是非联合类 没有用户声明的构造函数的类型,然后是每个非静态数据 T的成员和基类组件是值初始化的;
(强调我的)
在C ++ 11中:
对T类型的对象进行值初始化意味着:
- 如果T是a(可能是 cv-qualified)带有用户提供的构造函数的类类型(第9节) (12.1),然后调用T的默认构造函数(和 如果T没有可访问的默认值,则初始化是错误的 构造函数);
- 如果T是(可能是cv限定的)非联合类类型 没有用户提供的构造函数,那么对象就是 零初始化,如果T是隐式声明的默认构造函数 是非平凡的,这个构造函数被调用。
和
零初始化T类型的对象或引用意味着:
- 如果T是a 标量类型(3.9),对象设置为值0(零),取为 一个整数常量表达式,转换为T;
- 如果T是 (可能是cv限定的)非联合类类型,每个非静态数据 成员和每个基类子对象都是零初始化和填充 被初始化为零位;
注意:以下内容仅适用于C ++ 03 :
删除A
用户提供的构造函数,或将其更改为
A() : i() {}
当您初始化 B
时,
B* p = new B {};
值初始化其数据成员。由于A
具有默认构造函数,因此值初始化会导致对该调用的调用。但是该构造函数没有显式初始化A::i
,因此它得到 default-initialized ,对于int
,它意味着不执行初始化。
如果您没有为A
,然后提供默认构造函数,则在A
值初始化时,数据成员将被初始化为零。
答案 3 :(得分:0)
不需要将积分类型初始化为非默认构造函数中的值(因为您提供了构造函数)
将构造函数更改为A() : i(0) {}
。