名稱空間
變體
操作

初始化

來自 cppreference.com
< cpp‎ | 語言
 
 
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 前)
  • 如果初始化器是語法 (2)(4),則物件是列表初始化的。
(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 的初始化之前排序(如果程式啟動一個執行緒,則為 happens-before)。
(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-使用任何變數或函式,則在該翻譯單元中定義的非區域性變數可能永遠不會被初始化(這模擬了按需動態庫的行為)。然而,只要從翻譯單元中 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++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 270 C++98 類模板的靜態資料成員的初始化順序
未指定
除了顯式特化和定義外,指定為無序
指定為無序,除了
CWG 441 C++98 具有靜態儲存期的非區域性引用
並非總是在動態初始化之前初始化
被視為靜態初始化,總是在
動態初始化之前初始化
CWG 1415 C++98 塊作用域的 extern 變數
宣告可能是一個定義
禁止(此類宣告中不允許使用初始化器)
禁止(此類宣告中不允許
CWG 2599 C++98 不清楚在初始化器中評估函式
引數是否是初始化的一部分
它是初始化的一部分

[編輯] 另請參閱

C 文件 關於 初始化