命名空间
变体
操作

值初始化

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
跳转语句
函数
函数声明
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)
::(...) : 成员 () { ... } (3)
T 对象 {}; (4) (C++11 起)
T {} (5) (C++11 起)
new T {} (6) (C++11 起)
::(...) : 成员 {} { ... } (7) (C++11 起)

[编辑] 解释

值初始化在以下情形进行

1,5) 当以一个由空括号或花括号(C++11 起)组成的初始化器创建无名临时对象时;
2,6) 当通过 new 表达式以一个由空括号或花括号(C++11 起)组成的初始化器创建具有动态存储期的对象时;
3,7) 当非静态数据成员或基类使用一个由空括号或花括号(C++11 起)组成的成员初始化器进行初始化时;
4) 当声明具名对象(自动、静态或线程局部)时,其初始化器由一对花括号组成。

在所有情况下,若使用空的花括号对 {}T 为聚合类型,则进行聚合初始化,而非值初始化。

T 是一个没有默认构造函数但有接收 std::initializer_list 的构造函数的类类型,则进行列表初始化

(C++11 起)

值初始化的效果是

  • T 是(可为 cv 限定的)类类型
  • 否则,若 T 是数组类型,则数组的每个元素被值初始化。
  • 否则,对象被零初始化。

[编辑] 注意

语法 T object(); 并不初始化对象;它声明一个不接受参数并返回 T 的函数。在 C++11 前值初始化一个具名变量的方式是 T object = T();,这会值初始化一个临时对象然后复制初始化该对象:大多数编译器在这种情况下会优化掉复制

引用不能被值初始化。

如在函数风格转型中所述,若 T 指名数组类型,则禁止语法 T() (1),但允许 T{} (5)

所有标准容器(std::vectorstd::list 等)在以单个 size_type 参数构造或通过调用 resize() 扩容时,都会对其元素进行值初始化,除非它们的分配器自定义了 construct 的行为。

[编辑] 示例

#include <cassert>
#include <iostream>
#include <string>
#include <vector>
 
struct T1
{
    int mem1;
    std::string mem2;
    virtual void foo() {} // make sure T1 is not an aggregate
}; // implicit default constructor
 
struct T2
{
    int mem1;
    std::string mem2;
    T2(const T2&) {} // user-provided copy constructor
};                   // no default constructor
 
struct T3
{
    int mem1;
    std::string mem2;
    T3() {} // user-provided default constructor
};
 
std::string s{}; // class => default-initialization, the value is ""
 
int main()
{
    int n{};                // scalar => zero-initialization, the value is 0
    assert(n == 0);
    double f = double();    // scalar => zero-initialization, the value is 0.0
    assert(f == 0.0);
    int* a = new int[10](); // array => value-initialization of each element
    assert(a[9] == 0);      //          the value of each element is 0
    T1 t1{};                // class with implicit default constructor =>
    assert(t1.mem1 == 0);   //     t1.mem1 is zero-initialized, the value is 0
    assert(t1.mem2 == "");  //     t1.mem2 is default-initialized, the value is ""
//  T2 t2{};                // error: class with no default constructor
    T3 t3{};                // class with user-provided default constructor =>
    std::cout << t3.mem1;   //     t3.mem1 is default-initialized to indeterminate value
    assert(t3.mem2 == "");  //     t3.mem2 is default-initialized, the value is ""
    std::vector<int> v(3);  // value-initialization of each element
    assert(v[2] == 0);      // the value of each element is 0
    std::cout << '\n';
    delete[] a;
}

可能的输出

42

[编辑] 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 发布时的行为 正确的行为
CWG 178 C++98 没有值初始化;空初始化器调用默认
初始化(尽管 new T() 也进行零初始化)
空初始化器调用
值初始化
CWG 543 C++98 对于没有任何用户提供的构造函数的类对象,值初始化
等同于值
初始化每个子对象(这不一定会零
初始化一个带有用户提供默认构造函数的成员)
零初始化
整个对象,
然后调用
默认构造函数
CWG 1301 C++11 对具有已删除默认构造函数的联合体进行值初始化
会导致零初始化
它们是
默认初始化
CWG 1368 C++98 任何用户提供的构造函数都会导致
跳过零初始化
只有用户提供的
默认构造函数
会跳过零初始化
CWG 1502 C++11 对没有用户提供的默认构造函数的联合体进行值初始化
只对该对象进行零初始化,
尽管存在默认成员初始化器
在零初始化后
进行默认
初始化
CWG 1507 C++98 对于没有任何用户提供的构造函数的类对象,值初始化
当默认构造函数是平凡的时候,
用户提供的构造函数不会检查其有效性
平凡的有效性
默认构造函数
被检查
CWG 2820 C++98 零初始化之后的默认
初始化需要一个非平凡的构造函数
未要求
CWG 2859 C++98 类对象的值初始化可能涉及
零初始化,即使默认初始化
实际上并未选择用户提供的构造函数
没有
初始化
在这种情况下

[编辑] 参阅