默认初始化
来自 cppreference.cn
这是在没有初始化器的情况下构造对象时执行的初始化。
内容 |
[编辑] 语法
T 对象 ; |
(1) | ||||||||
new T |
(2) | ||||||||
[编辑] 解释
在三种情况下执行默认初始化
1) 当声明具有自动、静态或线程局部存储期的变量且没有初始化器时;
2) 当通过没有初始化器的 new 表达式 创建具有动态存储期的对象时;
3) 当基类或非静态数据成员未在构造函数初始化列表中提及,并且调用该构造函数时。
默认初始化的效果是
- 如果
T
是(可能带 cv 限定的)非 POD(直到 C++11) 类类型,则会考虑构造函数并针对空参数列表进行重载决议。 调用所选的构造函数(它是默认构造函数之一)以提供新对象的初始值; - 如果
T
是数组类型,则数组的每个元素都进行默认初始化; - 否则,不执行初始化(参见注解)。
[编辑] const 对象的默认初始化
如果程序要求默认初始化 const 限定类型 T
的对象,则 T 应该是const-默认可构造的类类型或其数组。
如果对 T
进行默认初始化会调用 T
的用户提供的构造函数(不是从基类继承的)(自 C++11 起)或者如果,则类类型 T
是 const-默认可构造的
仅当未使用初始化器时,才认为具有自动存储期的(可能带 cv 限定的)非 POD 类类型(或其数组)是默认初始化的。 具有动态存储期的标量和 POD 类型被认为未初始化(自 C++11 起,这种情况被重新归类为默认初始化的一种形式)。 |
(直到 C++11) |
|
(直到 C++11) |
(自 C++11 起) |
T
的每个潜在构造的基类都是 const-默认可构造的。
[编辑] 不确定值和错误值
当获取具有自动或动态存储期的对象的存储时,该对象具有不确定值。 如果未对对象执行初始化,则该对象将保留不确定值,直到该值被替换。 |
(直到 C++26) |
当获取具有自动或动态存储期的对象的存储时,构成对象存储的字节具有以下初始值
如果未对对象(包括子对象)执行初始化,则此类字节将保留其初始值,直到该值被替换。
|
(自 C++26 起) |
如果求值产生不确定值,则行为是未定义的。
如果求值产生错误值,则行为是错误的。 |
(自 C++26 起) |
[编辑] 特殊情况
以下类型是未初始化友好的
(自 C++17 起) |
- unsigned char
- char,如果其底层类型是 unsigned char
给定一个不确定或错误(自 C++26 起) 值 value,value 的未初始化结果值是
- 不确定值,如果 value 也是不确定值。
|
(自 C++26 起) |
如果求值 eval 产生未初始化友好类型的不确定或错误(自 C++26 起) 值 value,则在以下情况下,行为是良好定义的
- eval 是以下表达式和操作数之一的求值
- 在这种情况下,操作的结果是 value 的未初始化结果值。
- eval 是简单赋值运算符的右操作数的求值,其左操作数是未初始化友好类型的左值。
- 在这种情况下,左操作数引用的对象的值将替换为 value 的未初始化结果值。
- eval 是初始化未初始化友好类型的对象时的初始化表达式的求值。
(自 C++17 起) |
- 在这种情况下,该对象将被初始化为 value 的未初始化结果值。
转换未初始化友好类型的不确定值会产生不确定值。
转换未初始化友好类型的错误值会产生错误值,转换结果是被转换操作数的值。 |
(自 C++26 起) |
// Case 1: Uninitialized objects with dynamic storage duration // All C++ versions: indeterminate value + undefined behavior int f(bool b) { unsigned char* c = new unsigned char; unsigned char d = *c; // OK, “d” has an indeterminate value int e = d; // undefined behavior return b ? d : 0; // undefined behavior if “b” is true } // Case 2: Uninitialized objects with automatic storage duration // until C++26: indeterminate value + undefined behavior // since C++26: erroneous value + erroneous behavior int g(bool b) { unsigned char c; // “c” has an indeterminate/erroneous value unsigned char d = c; // no undefined/erroneous behavior, // but “d” has an indeterminate/erroneous value assert(c == d); // holds, but both integral promotions have // undefined/erroneous behavior int e = d; // undefined/erroneous behavior return b ? d : 0; // undefined/erroneous behavior if “b” is true } // Same as case 2 void h() { int d1, d2; // “d1” and “d2” have indeterminate/erroneous values int e1 = d1; // undefined/erroneous behavior int e2 = d1; // undefined/erroneous behavior assert(e1 == e2); // holds assert(e1 == d1); // holds, undefined/erroneous behavior assert(e2 == d1); // holds, undefined/erroneous behavior // no undefined/erroneous behavior, // but “d2” has an indeterminate/erroneous value std::memcpy(&d2, &d1, sizeof(int)); assert(e1 == d2); // holds, undefined/erroneous behavior assert(e2 == d2); // holds, undefined/erroneous behavior }
[编辑] 注解
引用和 const 标量对象不能默认初始化。
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__cpp_constexpr |
201907L |
(C++20) | 平凡默认初始化和 asm 声明在 constexpr 函数中 |
[编辑] 示例
运行此代码
#include <string> struct T1 { int mem; }; struct T2 { int mem; T2() {} // “mem” is not in the initializer list }; int n; // static non-class, a two-phase initialization is done: // 1) zero-initialization initializes n to zero // 2) default-initialization does nothing, leaving n being zero int main() { [[maybe_unused]] int n; // non-class, the value is indeterminate std::string s; // class, calls default constructor, the value is "" std::string a[2]; // array, default-initializes the elements, the value is {"", ""} // int& r; // Error: a reference // const int n; // Error: a const non-class // const T1 t1; // Error: const class with implicit default constructor [[maybe_unused]] T1 t1; // class, calls implicit default constructor const T2 t2; // const class, calls the user-provided default constructor // t2.mem is default-initialized }
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 178 | C++98 | 没有值初始化; 空初始化器调用默认初始化 (尽管 new T() 也执行零初始化) |
空初始化器调用 值初始化 |
CWG 253 | C++98 | const 对象的默认初始化可能无法 调用隐式声明的默认构造函数 |
如果所有子对象都已初始化,则允许 |
CWG 616 | C++98 | 任何未初始化对象的左值到右值转换始终是 UB 任何未初始化对象的左值到右值转换始终是 UB |
允许不确定的 unsigned char |
CWG 1787 | C++98 | 从寄存器中缓存的不确定的 unsigned char 读取是 UB 从寄存器中缓存的不确定的 unsigned char 读取是 UB |
已良好定义 |