命名空间
变体
操作

初始化

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

初始化 变量在构造时提供其初始值。

初始值可以在 声明符new 表达式 的初始化部分提供。它也发生在函数调用期间:函数参数和函数返回值也被初始化。

内容

[编辑] 初始化器

对于每个声明符,初始化器(如果存在)可以是以下之一

= 表达式 (1)
= {}
= { 初始化列表 }
= { 指定初始化列表 }
(2)

(自 C++20 起)
( 表达式列表 )
( 初始化列表 )
(3) (直到 C++11)
(自 C++11 起)
{}
{ 初始化列表 }
{ 指定初始化列表 }
(4) (自 C++11 起)
(自 C++11 起)
(自 C++20 起)
1) 复制初始化语法。
2) 聚合初始化语法。(直到 C++11)列表初始化语法。(自 C++11 起)
3) 直接初始化语法。
4) 列表初始化语法。
表达式 - 任何表达式(除了未加括号的 逗号表达式
表达式列表 - 一个用逗号分隔的表达式列表(除了未加括号的逗号表达式)
初始化列表 - 一个用逗号分隔的初始化子句列表(见下文)
指定初始化列表 - 一个用逗号分隔的 指定初始化子句 列表


一个初始化子句 可以是以下之一

表达式 (1)
{} (2)
{ 初始化列表 } (3)
{ 指定初始化列表 } (4) (自 C++20 起)

语法 (2-4) 统称为大括号括起来的初始化列表

[编辑] 初始化器语义

如果未为对象指定初始化器,则对象将进行 默认初始化。如果未为 引用 指定初始化器,则程序格式不正确。

如果为对象指定的初始化器为 ()(由于语法限制,不能出现在声明符中),则对象将进行 值初始化。如果为引用指定的初始化器为 (),则程序格式不正确。

初始化器的语义如下

  • 如果正在初始化的实体是引用,请参见 引用初始化
  • 否则,正在初始化的实体是一个对象。假设对象的类型为 T
  • 如果初始化器是语法 (2)
(直到 C++11)
(自 C++11 起)
#include <string>
 
std::string s1;           // default-initialization
std::string s2();         // NOT an initialization!
                          // actually declares a function “s2”
                          // with no parameter and returns std::string
std::string s3 = "hello"; // copy-initialization
std::string s4("hello");  // direct-initialization
std::string s5{'a'};      // list-initialization (since C++11)
 
char a[3] = {'a', 'b'}; // aggregate initialization
                        // (part of list initialization since C++11)
char& c = a[0];         // reference initialization

[编辑] 非局部变量

所有具有静态 存储期限 的非局部变量都在程序启动时初始化,在 main 函数 开始执行之前(除非延迟,见下文)。所有具有线程本地存储期限的非局部变量都在线程启动时初始化,在线程函数开始执行之前排序之前。对于这两类变量,初始化发生在两个不同的阶段

[编辑] 静态初始化

静态初始化有两种形式

1) 如果可能,应用 常量初始化
2) 否则,非局部静态和线程本地变量将 零初始化

实际上

  • 常量初始化通常在编译时应用。预先计算的对象表示存储为程序映像的一部分。如果编译器没有这样做,它仍然必须保证初始化在任何动态初始化之前发生。
  • 要零初始化的变量被放置在程序映像的 .bss 段中,该段在磁盘上不占用任何空间,并在加载程序时由操作系统清零。

[编辑] 动态初始化

在所有静态初始化完成后,非局部变量的动态初始化将在以下情况下发生

1) 无序动态初始化,它仅适用于(静态/线程本地)类模板 静态数据成员 变量模板(自 C++14 起),它们不是 显式特化。这种静态变量的初始化与所有其他动态初始化的顺序是不确定的 除非程序在变量初始化之前启动线程,在这种情况下它的初始化是不排序的(自 C++17 起)。这种线程本地变量的初始化与所有其他动态初始化的顺序是不排序的。
2) 部分有序动态初始化,它适用于所有不是隐式或显式实例化的特化的内联变量。如果部分有序 V 在每个翻译单元中都定义在有序或部分有序 W 之前,则 V 的初始化在 W 的初始化之前排序(或者发生在之前,如果程序启动了线程)。
(自 C++17 起)
3) 有序动态初始化,它适用于所有其他非局部变量:在一个翻译单元内,这些变量的初始化总是 排序,它们在源代码中出现的顺序完全相同。不同翻译单元中静态变量的初始化是随机排序的。不同翻译单元中线程本地变量的初始化是不排序的。

如果具有静态或线程存储期限的非局部变量的初始化通过异常退出,则会调用 std::terminate

[编辑] 早期动态初始化

如果满足以下两个条件,编译器可以将动态初始化的变量初始化为静态初始化的一部分(本质上是在编译时)

1) 初始化的动态版本不会在初始化之前更改任何其他命名空间范围对象的的值
2) 静态版本的初始化在已初始化变量中生成的值与动态初始化(如果所有不需要静态初始化的变量都被动态初始化)将生成的值相同。

由于以上规则,如果某个对象 o1 的初始化引用了一个命名空间范围对象 o2,该对象可能需要动态初始化,但定义在同一个翻译单元的后面,则o2 使用的值是完全初始化的 o2 的值(因为编译器将 o2 的初始化提升到编译时)还是仅仅零初始化的 o2 的值,这是不确定的。

inline double fd() { return 1.0; }
 
extern double d1;
 
double d2 = d1;   // unspecified:
                  // dynamically initialized to 0.0 if d1 is dynamically initialized, or
                  // dynamically initialized to 1.0 if d1 is statically initialized, or
                  // statically initialized to 0.0 (because that would be its value
                  // if both variables were dynamically initialized)
 
double d1 = fd(); // may be initialized statically or dynamically to 1.0

[编辑] 延迟动态初始化

动态初始化是否发生在 main 函数(对于静态变量)或线程的初始函数(对于线程本地变量)的第一个语句之前,还是延迟到之后,这是实现定义的。

如果 非内联变量的(自 C++17 起) 初始化被延迟到 main/线程函数的第一个语句之后,它发生在同一个翻译单元中定义的任何具有静态/线程存储期限的变量的第一个 ODR 使用 之前。如果给定翻译单元中没有使用任何变量或函数,则该翻译单元中定义的非局部变量可能永远不会被初始化(这模拟了按需动态库的行为)。但是,只要从一个翻译单元中使用任何内容,所有具有副作用的初始化或销毁的非局部变量都将被初始化,即使它们不在程序中使用。

如果内联变量的初始化被延迟,它发生在该特定变量的第一个 ODR 使用 之前。

(自 C++17 起)
// ============
// == File 1 ==
 
#include "a.h"
#include "b.h"
 
B b;
A::A() { b.Use(); }
 
// ============
// == File 2 ==
 
#include "a.h"
 
A a;
 
// ============
// == File 3 ==
 
#include "a.h"
#include "b.h"
 
extern A a;
extern B b;
 
int main()
{
    a.Use();
    b.Use();
}
 
// If a is initialized before main is entered, b may still be uninitialized
// at the point where A::A() uses it (because dynamic initialization is
// indeterminately sequenced across translation units)
 
// If a is initialized at some point after the first statement of main (which odr-uses
// a function defined in File 1, forcing its dynamic initialization to run),
// then b will be initialized prior to its use in A::A

[编辑] 静态局部变量

有关局部(即块作用域)静态和线程本地变量的初始化,请参见 静态局部变量

在具有 外部链接或内部链接 的变量的块作用域声明中不允许使用初始化器。这种声明必须与 extern 一起出现,并且不能是定义。

[编辑] 类成员

非静态数据成员可以使用 成员初始化列表默认成员初始化器 初始化。

[编辑] 注释

非局部变量的销毁顺序在 std::exit 中描述。

[编辑] 缺陷报告

以下更改行为的缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布的行为 正确行为
CWG 270 C++98 初始化类模板的静态数据成员的顺序
是不确定的
指定为无序,除了
显式特化和定义
CWG 441 C++98 具有静态存储期限的非局部引用
并不总是被初始化在动态初始化之前
被视为静态初始化,总是
在动态初始化之前初始化
CWG 1415 C++98 块作用域的 extern 变量
声明可以是定义
禁止(在这些声明中
不允许使用初始化器)
CWG 2599 C++98 尚不清楚在初始化器中评估函数
参数是否属于初始化的一部分
它是初始化的一部分

[编辑] 另请参阅

C 文档 针对 初始化