默认初始化
来自 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 起),或者如果
只有具有自动存储持续时间的(可能是 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 标量对象不能默认初始化。
功能测试宏 | 值 | 标准 | 特性 |
---|---|---|---|
__cpp_constexpr |
201907L |
(C++20) | constexpr 函数中的平凡默认初始化和 asm 声明 |
[编辑] 示例
运行此代码
#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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 178 | C++98 | 没有值初始化; 空初始化器调用默认初始化 (尽管 new T() 也执行零初始化) |
空初始化器调用 值初始化 |
CWG 253 | C++98 | const 对象的默认初始化不能 调用隐式声明的默认构造函数 |
如果所有子对象都已初始化,则允许 |
CWG 616 | C++98 | 任何未初始化对象的左值到右值转换总是未定义行为 未定义 |
允许不确定的 unsigned char |
CWG 1787 | C++98 | 从不确定的 unsigned char 读取 缓存到寄存器中是未定义行为 |
已明确定义 |