默认初始化
这是在不使用初始化器构造变量时执行的初始化。
目录 |
[编辑]语法
T 对象 ; | (1) | ||||||||
new T | (2) | ||||||||
[编辑]解释
默认初始化在三种情况下进行:
默认初始化的效果是:
- 如果
T
是非 POD (C++11 前)类类型,那么考虑各构造函数并实施针对空实参列表的重载决议。调用所选的构造函数(即默认构造函数之一),以提供新对象的初始值; - 如果
T
是数组类型,那么该数组的每个元素都被默认初始化; - 否则不进行初始化(见注解)。
当未使用初始化器时,仅有具有自动存储期的(可能 cv 限定的)非 POD 类类型(或其数组)认为被默认初始化。具有动态存储期的标量和 POD 类型则认为未被初始化(从 C++11 开始,这种情况被归类为默认初始化的一种形式)。 | (C++11 前) |
| (C++11 前) |
(C++11 起) |
T
的每个潜在构造的基类均可 const 默认构造。
[编辑]不确定值和错误值
在获取到具有自动或动态存储期的对象的存储时,该对象具有不确定值。 如果不对对象进行任何初始化,那么该对象具有不确定值,直到该值被替换。 | (C++26 前) |
在获取到具有自动或动态存储期的对象的存储时,该对象的存储包含的直接具有以下初始值:
如果不对对象(包括子对象)进行任何初始化,那么此类字节会保留它们的初始值,直到该值被替换。
| (C++26 起) |
如果求值产生了不确定值,那么行为未定义。
如果求值产生了错误值,那么行为错误。 | (C++26 起) |
[编辑]特殊情况
以下类型是未初始化友好类型:
(C++17 起) |
- unsignedchar
- char,如果它的底层类型是 unsignedchar
给定某个不确定值或错误值(C++26 起)value,value 的未初始化结果值 是
- 某个不确定值,如果 value 也是不确定值。
| (C++26 起) |
如果某个求值 eval 产生了具有未初始化友好类型的不确定值或错误值(C++26 起)value,那么在以下情况下行为具有良好定义:
- eval 是对以下表达式和操作数的求值:
- eval 是对简单赋值运算符的右操作数的求值,并且 该运算符的左操作数是具有未初始化友好类型的左值。
- 这种情况下,左操作数指代的对象的值会被替换成 value 的未初始化结果值。
- eval 是对初始化表达式的求值,并且要初始化的是具有未初始化友好类型的对象。
(C++17 起) |
- 这种情况下,该对象会被初始化为 value 的未初始化结果值。
从具有未初始化友好类型的不确定值转换会得到一个不确定值。
从具有未初始化友好类型的错误值转换会得到一个错误值,转换结果是转换后的操作数的值。 | (C++26 起) |
// 情况 1:具有动态存储期的未初始化对象// 所有 C++ 版本:不确定值+未定义行为int f(bool b){unsignedchar* c = new unsignedchar;unsignedchar d =*c;// OK, “d” 具有不确定值int e = d;// 未定义行为return b ? d :0;// “b” 是 true 时是未定义行为} // 情况 2:具有自动存储期的未初始化对象// C++26 前:不确定值+未定义行为// C++26 后:错误值+错误行为int g(bool b){unsignedchar c;// “c” 具有不确定值/错误值 unsignedchar d = c;// 没有未定义行为/错误行为,// 但 “d” 具有不确定值/错误值 assert(c == d);// 成立,但两个整数提升都有未定义行为/错误行为 int e = d;// 未定义行为/错误行为return b ? d :0;// “b” 是 true 时是未定义/错误行为} // 与情况 2 相同void h(){int d1, d2;// “d1” 和 “d2” 都具有不确定值/错误值int e1 = d1;// 未定义行为/错误行为int e2 = d1;// 未定义行为/错误行为 assert(e1 == e2);// 成立assert(e1 == d1);// 成立,未定义行为/错误行为assert(e2 == d1);// 成立,未定义行为/错误行为 // 没有未定义行为/错误行为,// 但 “d2” 具有不确定值/错误值std::memcpy(&d2, &d1, sizeof(int)); assert(e1 == d2);// 成立,未定义行为/错误行为assert(e2 == d2);// 成立,未定义行为/错误行为}
[编辑]注解
不能默认初始化引用和 const 标量对象。
功能特性测试宏 | 值 | 标准 | 功能特性 |
---|---|---|---|
__cpp_constexpr | 201907L | (C++20) | constexpr 函数中的平凡默认初始化和汇编声明 |
[编辑]示例
#include <string> struct T1 {int mem;}; struct T2 {int mem; T2(){}// “mem” 不在初始化器列表中}; int n;// 静态非类,进行两阶段初始化:// 1) 零初始化将 n 初始化为零// 2) 默认初始化不做任何事,令 n 保留为零 int main(){[[maybe_unused]]int n;// 非类,值不确定std::string s;// 类,调用默认构造函数,值是 ""std::string a[2];// 数组,默认初始化其各元素,值是 {"", ""}// int& r; // 错误:引用// const int n; // 错误:const 的非类// const T1 t1; // 错误:const 的带隐式默认构造函数的类[[maybe_unused]] T1 t1;// 类,调用隐式默认构造函数const T2 t2;// const 类,调用用户提供的默认构造函数// t2.mem 被默认初始化}
[编辑]缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 178 | C++98 | 没有值初始化;空初始化器会调用默认初始化 (尽管 new T() 也会进行零初始化) | 空初始化器会调用值初始化 |
CWG 253 | C++98 | const 对象的默认初始化不能调用隐式声明的默认构造函数 | 所有子对象已初始化时允许 |
CWG 616 | C++98 | 任何未初始化对象的左值到右值的转换都是未定义行为 | 允许值不确定的 unsignedchar |
CWG 1787 | C++98 | 从在寄存器缓存的不确定的 unsignedchar 读取是未定义行为 | 赋予良好定义 |