值初始化
来自 cppreference.cn
这是当对象以一个空的初始化器构造时所进行的初始化。
目录 |
[编辑] 语法
T () |
(1) | ||||||||
new T () |
(2) | ||||||||
类:: 类( ...) : 成员 () { ... } |
(3) | ||||||||
T 对象 {}; |
(4) | (C++11 起) | |||||||
T {} |
(5) | (C++11 起) | |||||||
new T {} |
(6) | (C++11 起) | |||||||
类:: 类( ...) : 成员 {} { ... } |
(7) | (C++11 起) | |||||||
[编辑] 解释
值初始化在以下情形进行
1,5) 当以一个由空括号或花括号(C++11 起)组成的初始化器创建无名临时对象时;
4) 当声明具名对象(自动、静态或线程局部)时,其初始化器由一对花括号组成。
在所有情况下,若使用空的花括号对 {}
且 T
为聚合类型,则进行聚合初始化,而非值初始化。
若 |
(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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
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 | 类对象的值初始化可能涉及 零初始化,即使默认初始化 实际上并未选择用户提供的构造函数 |
没有 初始化 在这种情况下 |