命名空间
变体
操作

值初始化

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

这是当使用空初始化器构造对象时执行的初始化。

目录

[编辑] 语法

T () (1)
new T () (2)
Class::Class(...) : member () { ... } (3)
T object {}; (4) (since C++11)
T {} (5) (since C++11)
new T {} (6) (since C++11)
Class::Class(...) : member {} { ... } (7) (since C++11)

[编辑] 解释

值初始化在以下情况下执行

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

在所有情况下,如果使用空花括号 {} 并且 T 是聚合类型,则执行聚合初始化而不是值初始化。

如果 T 是没有默认构造函数但具有接受 std::initializer_list 的构造函数的类类型,则执行列表初始化

(since 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++ 标准。

DR 应用于 已发布行为 正确行为
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 类对象的值初始化可能涉及
零初始化,即使默认初始化
实际上没有选择用户提供的构造函数
没有
零初始化
在这种情况下

[编辑] 参见