命名空间
变体
操作

默认初始化

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
 
 

这是对象在没有初始化器的情况下构造时执行的初始化。

目录

[编辑] 语法

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 前)
  • T 的每个直接非静态数据成员 M 都是类类型 X(或其数组),X 是 const 默认可构造的,并且
  • T 没有直接变体成员,并且
(C++11 前)
  • T 的每个直接非变体非静态数据成员 M 都有一个默认成员初始化器,或者如果 M 是类类型 X(或其数组),X 是 const 默认可构造的,
  • 如果 T 是至少有一个非静态数据成员的 union,则恰好一个变体成员具有默认成员初始化器,
  • 如果 T 不是 union,对于每个至少有一个非静态数据成员的匿名 union 成员(如果有),恰好一个非静态数据成员具有默认成员初始化器,并且
(C++11 起)

T 的每个可能构造的基类都是 const 默认可构造的。

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

当为具有自动或动态存储持续时间的对象分配存储空间时,该对象具有一个不确定的值

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

(直到 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 是初始化表达式的评估,用于初始化未初始化友好类型的对象。
  • 如果被初始化的对象不是 std::byte 类型,则 value 不能是 std::byte 类型。
(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 读取
缓存到寄存器中是未定义行为
已明确定义

[编辑] 另请参阅