值初始化
来自 cppreference.cn
这是当使用空初始化器构造对象时执行的初始化。
目录 |
[编辑] 语法
T () |
(1) | ||||||||
new T () |
(2) | ||||||||
Class:: Class( ...) : member () { ... } |
(3) | ||||||||
T object {}; |
(4) | (since C++11) | |||||||
T {} |
(5) | (since C++11) | |||||||
new T {} |
(6) | (since C++11) | |||||||
Class:: Class( ...) : member {} { ... } |
(7) | (since C++11) | |||||||
[编辑] 解释
值初始化在以下情况下执行
1,5) 当使用由一对空括号 或花括号(since C++11)组成的初始化器创建无名临时对象时;
4) 当声明一个具名对象(自动、静态或线程局部)且初始化器由一对花括号组成时。
在所有情况下,如果使用空花括号 {}
并且 T
是聚合类型,则执行聚合初始化而不是值初始化。
如果 |
(since C++11) |
值初始化的效果是
- 如果
T
是(可能带有 cv 限定符的)类类型
- 否则,如果
T
是数组类型,则数组的每个元素都会被值初始化。 - 否则,对象会被零初始化。
[编辑] 备注
语法 T object(); 不会初始化对象;它声明一个不接受参数并返回 T
的函数。在 C++11 之前值初始化命名变量的方法是 T object = T();,它值初始化一个临时对象,然后复制初始化该对象:在这种情况下,大多数编译器会优化掉复制操作。
引用不能被值初始化。
如函数式转型中所述,如果 T
命名一个数组类型,则语法 T() (1) 是禁止的,而 T{} (5) 是允许的。
所有标准容器(std::vector、std::list 等)在用单个 size_type
参数构造或通过调用 resize() 增长时,都会值初始化它们的元素,除非它们的分配器自定义了 construct 的行为。
[编辑] 示例
运行此代码
#include <cassert> #include <iostream> #include <string> #include <vector> struct T1 { int mem1; std::string mem2; virtual void foo() {} // make sure T1 is not an aggregate }; // implicit default constructor struct T2 { int mem1; std::string mem2; T2(const T2&) {} // user-provided copy constructor }; // no default constructor struct T3 { int mem1; std::string mem2; T3() {} // user-provided default constructor }; std::string s{}; // class => default-initialization, the value is "" int main() { int n{}; // scalar => zero-initialization, the value is 0 assert(n == 0); double f = double(); // scalar => zero-initialization, the value is 0.0 assert(f == 0.0); int* a = new int[10](); // array => value-initialization of each element assert(a[9] == 0); // the value of each element is 0 T1 t1{}; // class with implicit default constructor => assert(t1.mem1 == 0); // t1.mem1 is zero-initialized, the value is 0 assert(t1.mem2 == ""); // t1.mem2 is default-initialized, the value is "" // T2 t2{}; // error: class with no default constructor T3 t3{}; // class with user-provided default constructor => std::cout << t3.mem1; // t3.mem1 is default-initialized to indeterminate value assert(t3.mem2 == ""); // t3.mem2 is default-initialized, the value is "" std::vector<int> v(3); // value-initialization of each element assert(v[2] == 0); // the value of each element is 0 std::cout << '\n'; delete[] a; }
可能的输出
42
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 178 | C++98 | 没有值初始化;空初始化器调用默认 初始化(尽管 new T() 也执行零初始化) |
空初始化器调用 值初始化 |
CWG 543 | C++98 | 对没有用户提供的构造函数的类对象进行值初始化 等同于值 初始化每个子对象(不需要零 初始化具有用户提供的默认构造函数的成员) |
零初始化 整个对象, 然后调用 默认构造函数 |
CWG 1301 | C++11 | 具有已删除默认构造函数的联合体的价值初始化 导致零初始化 |
它们是 默认初始化 |
CWG 1368 | C++98 | 任何用户提供的构造函数都会导致 跳过零初始化 |
仅用户提供的 默认构造函数 跳过零初始化 |
CWG 1502 | C++11 | 值初始化没有用户提供的构造函数的联合体 默认构造函数仅零初始化了 对象,尽管有默认成员初始化器 |
执行默认 初始化之后 零初始化 |
CWG 1507 | C++98 | 对没有用户提供的构造函数的类对象进行值初始化 用户提供的构造函数未检查有效性 当后者是平凡的时,默认构造函数的有效性 |
平凡的有效性 默认构造函数 被检查 |
CWG 2820 | C++98 | 继零初始化之后的默认初始化 需要一个非平凡的构造函数 |
不需要 |
CWG 2859 | C++98 | 类对象的值初始化可能涉及 零初始化,即使默认初始化 实际上没有选择用户提供的构造函数 |
没有 零初始化 在这种情况下 |