Value-initialization
这是在用空初始化器构造对象时执行的初始化操作。
目录 |
语法
T
()
|
(1) | ||||||||
new
T
()
|
(2) | ||||||||
Class
::
Class
(
...
)
:
member
()
{
...
}
|
(3) | ||||||||
T
object
{};
|
(4) | (C++11 起) | |||||||
T
{}
|
(5) | (C++11 起) | |||||||
new
T
{}
|
(6) | (C++11 起) | |||||||
Class
::
Class
(
...
)
:
member
{}
{
...
}
|
(7) | (C++11 起) | |||||||
说明
值初始化在以下情况下执行:
在所有情况下,若使用空大括号对
{}
且
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() {} // 确保 T1 不是聚合类型 }; // 隐式默认构造函数 struct T2 { int mem1; std::string mem2; T2(const T2&) {} // 用户提供的拷贝构造函数 }; // 无默认构造函数 struct T3 { int mem1; std::string mem2; T3() {} // 用户提供的默认构造函数 }; std::string s{}; // 类类型 => 默认初始化,值为 "" int main() { int n{}; // 标量类型 => 零初始化,值为 0 assert(n == 0); double f = double(); // 标量类型 => 零初始化,值为 0.0 assert(f == 0.0); int* a = new int[10](); // 数组 => 每个元素的值初始化 assert(a[9] == 0); // 每个元素的值为 0 T1 t1{}; // 具有隐式默认构造函数的类 => assert(t1.mem1 == 0); // t1.mem1 被零初始化,值为 0 assert(t1.mem2 == ""); // t1.mem2 被默认初始化,值为 "" // T2 t2{}; // 错误:没有默认构造函数的类 T3 t3{}; // 具有用户提供默认构造函数的类 => std::cout << t3.mem1; // t3.mem1 被默认初始化为不确定值 assert(t3.mem2 == ""); // t3.mem2 被默认初始化,值为 "" std::vector<int> v(3); // 每个元素的值初始化 assert(v[2] == 0); // 每个元素的值为 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 |
类对象的值初始化可能涉及零初始化,
即使默认初始化实际上未选择用户提供的构造函数 |
此情况下不进行
零初始化 |