初始化
初始化 变量在构造时提供其初始值。
初始值可以在 声明符 或 new 表达式 的初始化部分提供。它也发生在函数调用期间:函数参数和函数返回值也被初始化。
内容 |
[编辑] 初始化器
对于每个声明符,初始化器(如果存在)可以是以下之一
= 表达式 |
(1) | ||||||||
= {} = { 初始化列表 } = { 指定初始化列表 } |
(2) | (自 C++20 起) | |||||||
( 表达式列表 ) ( 初始化列表 ) |
(3) | (直到 C++11) (自 C++11 起) | |||||||
{} { 初始化列表 } { 指定初始化列表 } |
(4) | (自 C++11 起) (自 C++11 起) (自 C++20 起) | |||||||
表达式 | - | 任何表达式(除了未加括号的 逗号表达式) |
表达式列表 | - | 一个用逗号分隔的表达式列表(除了未加括号的逗号表达式) |
初始化列表 | - | 一个用逗号分隔的初始化子句列表(见下文) |
指定初始化列表 | - | 一个用逗号分隔的 指定初始化子句 列表 |
一个初始化子句 可以是以下之一
表达式 | (1) | ||||||||
{}
|
(2) | ||||||||
{ 初始化列表 } |
(3) | ||||||||
{ 指定初始化列表 } |
(4) | (自 C++20 起) | |||||||
语法 (2-4) 统称为大括号括起来的初始化列表。
[编辑] 初始化器语义
如果未为对象指定初始化器,则对象将进行 默认初始化。如果未为 引用 指定初始化器,则程序格式不正确。
如果为对象指定的初始化器为 ()(由于语法限制,不能出现在声明符中),则对象将进行 值初始化。如果为引用指定的初始化器为 (),则程序格式不正确。
初始化器的语义如下
- 如果正在初始化的实体是引用,请参见 引用初始化。
- 否则,正在初始化的实体是一个对象。假设对象的类型为
T
- 如果初始化器是语法 (1),则对象将进行 复制初始化。
(直到 C++11) | |
|
(自 C++11 起) |
- 如果初始化器是 (3) 语法,则对象将 直接初始化。
#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 函数 开始执行之前(除非延迟,见下文)。所有具有线程本地存储期限的非局部变量都在线程启动时初始化,在线程函数开始执行之前排序之前。对于这两类变量,初始化发生在两个不同的阶段
[编辑] 静态初始化
静态初始化有两种形式
实际上
- 常量初始化通常在编译时应用。预先计算的对象表示存储为程序映像的一部分。如果编译器没有这样做,它仍然必须保证初始化在任何动态初始化之前发生。
- 要零初始化的变量被放置在程序映像的
.bss
段中,该段在磁盘上不占用任何空间,并在加载程序时由操作系统清零。
[编辑] 动态初始化
在所有静态初始化完成后,非局部变量的动态初始化将在以下情况下发生
2) 部分有序动态初始化,它适用于所有不是隐式或显式实例化的特化的内联变量。如果部分有序 V 在每个翻译单元中都定义在有序或部分有序 W 之前,则 V 的初始化在 W 的初始化之前排序(或者发生在之前,如果程序启动了线程)。
|
(自 C++17 起) |
如果具有静态或线程存储期限的非局部变量的初始化通过异常退出,则会调用 std::terminate。
[编辑] 早期动态初始化
如果满足以下两个条件,编译器可以将动态初始化的变量初始化为静态初始化的一部分(本质上是在编译时)
由于以上规则,如果某个对象 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 文档 针对 初始化
|