命名空间
变体
操作

默认初始化

来自 cppreference.com
< cpp‎ | 语言
 
 
C++ 语言
一般主题
流程控制
条件执行语句
if
迭代语句(循环)
for
range-for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常说明 (直到 C++17*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储持续时间说明符
初始化
 
 

这是在用无初始化器构造对象时执行的初始化。

内容

[编辑] 语法

T 对象 ; (1)
new T (2)

[编辑] 解释

默认初始化在三种情况下执行

1) 当一个具有自动、静态或线程局部存储持续时间的变量被声明但没有初始化器时;
2) 当一个具有动态存储持续时间的对象通过一个new 表达式创建,但没有初始化器时;
3) 当一个基类或一个非静态数据成员在构造函数初始化列表中没有被提及,并且该构造函数被调用时。

默认初始化的效果是

  • 如果 T 是一个(可能限定为 cv 的)非 POD(直到 C++11) 类类型,则会考虑构造函数,并针对空参数列表进行重载解析。选择的构造函数(是默认构造函数之一)被调用,以提供新对象的初始值;
  • 如果 T 是一个数组类型,则数组的每个元素都会被默认初始化;
  • 否则,不会执行初始化(参见注释)。

[编辑] const 对象的默认初始化

如果程序要求对一个const 限定类型 T 的对象进行默认初始化,则 T 必须是一个const-default-constructible 类类型或其数组。

如果对 T 进行默认初始化将调用 T 的用户提供的构造函数(而不是从基类继承的)(从 C++11 开始),或者如果

只有具有自动存储持续时间的(可能限定为 cv 的)非 POD 类类型(或其数组)在没有初始化器的情况下被认为是默认初始化的。具有动态存储持续时间的标量和 POD 类型被认为是未初始化的(从 C++11 开始,这种情况被重新归类为默认初始化的一种形式)。

(直到 C++11)
  • T 的每个直接非静态数据成员 M 都是类类型 X(或其数组),X 是 const-default-constructible,并且
  • T 没有直接变体成员,并且
(直到 C++11)
  • T 的每个直接非变体非静态数据成员 M 都具有一个默认成员初始化器,或者,如果 M 是类类型 X(或其数组),X 是 const-default-constructible,
  • 如果 T 是一个至少具有一个非静态数据成员的联合,则恰好一个变体成员具有一个默认成员初始化器,
  • 如果 T 不是一个联合,则对于每个至少具有一个非静态数据成员的匿名联合成员(如果有),恰好一个非静态数据成员具有一个默认成员初始化器,并且
(从 C++11 开始)

T 的每个可能构造的基类都是 const-default-constructible。

[编辑] 不确定的值和错误的值

当为具有自动或动态存储期的对象获取存储空间时,该对象具有不确定的值

如果未对对象进行初始化,则该对象会保留不确定的值,直到该值被替换。

(直到 C++26)

当为具有自动或动态存储期的对象获取存储空间时,构成该对象存储空间的字节具有以下初始值

  • 如果对象具有动态存储期,或者与变量或函数参数相关联,其第一个声明标记为[[indeterminate]],则这些字节具有不确定的值
  • 否则,这些字节具有错误的值,其中每个值由实现独立于程序的状态确定。

如果未对对象(包括子对象)进行初始化,则此类字节会保留其初始值,直到该值被替换。

  • 如果值表示中的任何位具有不确定的值,则该对象具有不确定的值
  • 否则,如果值表示中的任何位具有错误的值,则该对象具有错误的值
(自 C++26 起)

如果求值产生不确定的值,则行为是未定义的

如果求值产生错误的值,则行为是错误的

(自 C++26 起)

[编辑] 特殊情况

以下类型是未初始化友好的

(自 C++17 起)
  • unsigned char
  • char,如果其底层类型是unsigned char

给定一个不确定的或错误的(自 C++26 起)valuevalue未初始化结果值

  • 不确定的值,如果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) 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++ 标准。

DR 应用于 发布的行为 正确行为
CWG 178 C++98 没有值初始化;
空初始化器调用默认初始化
(尽管new T() 也执行零初始化)
空初始化器调用
值初始化
CWG 253 C++98 const 对象的默认初始化不能
调用隐式声明的默认构造函数
如果所有子对象都被初始化,则允许
CWG 616 C++98 任何
未初始化对象的左值到右值转换始终是 UB
允许不确定的unsigned char
CWG 1787 C++98 从不确定的unsigned char
缓存到寄存器中是 UB
变为明确定义

[编辑] 另请参见