Default-initialization
这是在对象构造时未提供初始化器所执行的初始化操作。
目录 |
语法
T 对象
;
|
(1) | ||||||||
new
T
|
(2) | ||||||||
说明
默认初始化在三种情况下执行:
默认初始化的效果是:
-
若
T是(可能带有 cv 限定符的) 非 POD (C++11 前) 类类型,则考虑其构造函数并对空实参列表执行 重载决议 。被选中的构造函数(即 默认构造函数 之一)将用于为新对象提供初始值; -
若
T是数组类型,则数组的每个元素均进行默认初始化; - 否则,不执行任何初始化(参见 备注 )。
|
当未使用初始化式时,仅具有自动存储期的(可能带有 cv 限定符的)非 POD 类类型(或其数组)被视为默认初始化。具有动态存储期的标量和 POD 类型被视为未初始化(自 C++11 起,此情况被重新分类为默认初始化的一种形式)。 |
(until C++11) |
|
(C++11 前) |
| (C++11 起) |
T
的每个
潜在构造
基类都是可常量默认构造的。
未确定值与错误值
|
当获取具有自动或动态存储期的对象存储空间时,该对象具有 不确定值 。 如果未对对象执行初始化,该对象将保持不确定值直至该值被替换。 |
(C++26 前) |
|
当获取具有自动或动态存储期的对象存储空间时,构成该对象存储空间的字节具有以下初始值:
如果未对对象(包括 子对象 )执行初始化,此类字节将保持其初始值直至该值被替换。
|
(C++26 起) |
若求值产生不确定值,则行为是 未定义的 。
|
若求值产生错误值,则行为是 错误的 。 |
(C++26 起) |
特殊情况
以下类型是 未初始化友好的 :
| (自 C++17 起) |
- unsigned char
- char ,若其底层类型为 unsigned char
给定一个不确定的 或错误的 (自 C++26 起) 值 value ,其 未初始化结果值 为:
- 一个不确定值,如果 value 同样是一个不确定值。
|
(自 C++26 起) |
若求值 eval 产生未初始化友好类型的不确定 或错误 (自 C++26 起) 值 value ,则在以下情形中行为是明确定义的:
- eval 是以下表达式和操作数之一的求值过程:
-
- 条件表达式的第二或第三操作数。
- 逗号表达式的右操作数。
-
整型转换、显式转换或转换为未初始化友好类型的
static_cast的操作数。 - 弃值表达式。
- 在此情况下,该操作的结果是 value 的未初始化结果值。
- eval 是对 简单赋值运算符 右操作数的求值,该运算符的左操作数是未初始化友好类型的左值。
- 在此情况下,左操作数所引用对象的值将被 value 的未初始化结果值所替换。
- eval 是在初始化未初始化友好类型对象时,对初始化表达式的求值过程。
| (自 C++17 起) |
- 在此情况下,该对象被初始化为 value 的未初始化结果值。
转换未初始化友好类型的不确定值会产生不确定值。
|
转换未初始化友好类型的错误值会产生错误值,转换结果是转换后操作数的值。 |
(since C++26) |
// 案例1:动态存储期的未初始化对象 // 所有C++版本:不确定值 + 未定义行为 int f(bool b) { unsigned char* c = new unsigned char; unsigned char d = *c; // 正确,“d”具有不确定值 int e = d; // 未定义行为 return b ? d : 0; // 当“b”为true时产生未定义行为 } // 案例2:自动存储期的未初始化对象 // C++26之前:不确定值 + 未定义行为 // C++26起:错误值 + 错误行为 int g(bool b) { unsigned char c; // “c”具有不确定/错误值 unsigned char 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 函数中的平凡默认初始化与 asm-declaration |
示例
#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 T1 t1; // 错误:具有隐式默认构造函数的常量类 [[maybe_unused]] T1 t1; // 类类型,调用隐式默认构造函数 const T2 t2; // 常量类类型,调用用户提供的默认构造函数 // t2.mem执行默认初始化 }
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
| DR | 适用版本 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 178 | C++98 |
不存在值初始化;
空初始化式调用默认初始化 (尽管 new T ( ) 仍会执行零初始化) |
空初始化式调用
值初始化 |
| CWG 253 | C++98 |
const对象的默认初始化不能
调用隐式声明的默认构造函数 |
当所有子对象均被初始化时允许 |
| CWG 616 | C++98 |
任何未初始化对象的左值到右值转换
均属未定义行为 |
允许 indeterminate unsigned char |
| CWG 1787 | C++98 |
读取缓存在寄存器中的 indeterminate
unsigned
char
属于未定义行为 |
明确定义为合法行为 |