預設初始化
這是當物件在建構時沒有提供初始器(initializer)所執行的初始化。
目錄 |
[編輯] 語法
T 物件 ; |
(1) | ||||||||
new T |
(2) | ||||||||
[編輯] 說明
預設初始化會在下列三種情況下執行:
預設初始化的效果如下:
- 若
T是(可能經 cv 限定的)非 POD(C++11 前) 類別型別,則考慮該類別的建構函式,並對空參數列表進行 多載解析 (overload resolution)。選中的建構函式(即 預設建構函式 之一)將被呼叫以提供新物件的初始值; - 若
T是陣列型別,則陣列中的每個元素都會被預設初始化; - 否則,不執行任何初始化(參見 備註)。
[編輯] const 物件的預設初始化
若程式要求對 const 限定型別 T 的物件進行預設初始化,則 T 必須是「const-預設可建構」(const-default-constructible) 的類別型別或其陣列。
若類別型別 T 的預設初始化會調用 T 的使用者提供建構函式 (非繼承自基底類別)(C++11 起),或滿足以下條件,則該類別為 const-預設可建構:
|
(歷史備註)在 C++11 之前,只有非 POD 類別型別(或其陣列)的自動儲存期物件在無初始器時被視為預設初始化。純量與 POD 型別的動態儲存期物件被視為未初始化(自 C++11 起,此情況被重新歸類為預設初始化的一種形式)。 |
(直到 C++11) |
|
(直到 C++11) |
| (C++11 起) |
T 的每個 潛在建構 (potentially constructed) 基底類別均為 const-預設可建構。
[編輯] 不確定值與錯誤值
|
當獲得具有自動或動態儲存期之物件的儲存空間時,該物件具有「不確定值」(indeterminate value)。 若物件未進行任何初始化,該物件將保持不確定值,直到該值被替換為止。 |
(直到 C++26) |
|
當獲得具有自動或動態儲存期之物件的儲存空間時,組成該物件儲存空間的位元組具有下列初始值:
若未對物件(包含其 子物件)執行初始化,則此類位元組將保留其初始值直到被替換。
|
(C++26 起) |
若求值產生不確定值,則其行為為 未定義 (undefined)。
|
若求值產生錯誤值,則其行為為 錯誤的 (erroneous)。 |
(C++26 起) |
[編輯] 特殊情況
下列型別被稱為「未初始化友善」(uninitialized-friendly):
| (自 C++17 起) |
- unsigned char
- char,若其底層型別為 unsigned char
給定一個不確定或錯誤(C++26 起)值 value,value 的「未初始化結果值」(uninitialized result value) 為:
- 若 value 亦為不確定值,則為不確定值。
|
(C++26 起) |
若求值 eval 產生了未初始化友善型別的不確定或錯誤(C++26 起)值 value,則在下列情況下行為良好定義:
- eval 是下列表達式與運算元的求值:
- 條件運算子的第二或第三運算元。
- 逗號運算子的右運算元。
- 整數轉換、明確轉型 (explicit cast) 或
static_cast至未初始化友善型別的運算元。 - 棄值表達式 (discarded-value expression)。
- 在此情況下,運算的結果為 value 的未初始化結果值。
- eval 是 簡單指派運算子右運算元的求值,且其左運算元為未初始化友善型別的左值。
- 在此情況下,左運算元所指物件的值會被替換為 value 的未初始化結果值。
- eval 是初始化未初始化友善型別物件時的初始化表達式求值。
| (自 C++17 起) |
- 在此情況下,該物件被初始化為 value 的未初始化結果值。
轉換未初始化友善型別的不確定值會產生不確定值。
|
轉換未初始化友善型別的錯誤值會產生錯誤值,且轉換結果即為該被轉換運算元的值。 |
(C++26 起) |
// Case 1: Uninitialized objects with dynamic storage duration // All C++ versions: indeterminate value + undefined behavior int f(bool b) { unsigned char* c = new unsigned char; unsigned char d = *c; // OK, “d” has an indeterminate value int e = d; // undefined behavior return b ? d : 0; // undefined behavior if “b” is true } // Case 2: Uninitialized objects with automatic storage duration // until C++26: indeterminate value + undefined behavior // since C++26: erroneous value + erroneous behavior int g(bool b) { unsigned char c; // “c” has an indeterminate/erroneous value unsigned char d = c; // no undefined/erroneous behavior, // but “d” has an indeterminate/erroneous value assert(c == d); // holds, but both integral promotions have // undefined/erroneous behavior int e = d; // undefined/erroneous behavior return b ? d : 0; // undefined/erroneous behavior if “b” is true } // Same as case 2 void h() { int d1, d2; // “d1” and “d2” have indeterminate/erroneous values int e1 = d1; // undefined/erroneous behavior int e2 = d1; // undefined/erroneous behavior assert(e1 == e2); // holds assert(e1 == d1); // holds, undefined/erroneous behavior assert(e2 == d1); // holds, undefined/erroneous behavior // no undefined/erroneous behavior, // but “d2” has an indeterminate/erroneous value std::memcpy(&d2, &d1, sizeof(int)); assert(e1 == d2); // holds, undefined/erroneous behavior assert(e2 == d2); // holds, undefined/erroneous behavior }
[編輯] 備註
參考與 const 純量物件無法被預設初始化。
| 特性測試巨集 | 數值 | 標準 | 功能 |
|---|---|---|---|
__cpp_constexpr |
201907L |
(C++20) | constexpr 函式中的平凡預設初始化與 asm-宣告 |
[編輯] 範例
#include <string> struct T1 { int mem; }; struct T2 { int mem; T2() {} // “mem” is not in the initializer list }; int n; // static non-class, a two-phase initialization is done: // 1) zero-initialization initializes n to zero // 2) default-initialization does nothing, leaving n being zero int main() { [[maybe_unused]] int n; // non-class, the value is indeterminate std::string s; // class, calls default constructor, the value is "" std::string a[2]; // array, default-initializes the elements, the value is {"", ""} // int& r; // Error: a reference // const int n; // Error: a const non-class // const T1 t1; // Error: const class with implicit default constructor [[maybe_unused]] T1 t1; // class, calls implicit default constructor const T2 t2; // const class, calls the user-provided default constructor // t2.mem is default-initialized }
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。
| DR | 應用於 | 出版時的行為 | 正確的行為 |
|---|---|---|---|
| CWG 178 | C++98 | 不存在值初始化 (value-initialization); 空初始器會調用預設初始化 (儘管 new T() 也會執行零初始化) |
空初始器調用 值初始化 |
| CWG 253 | C++98 | const 物件的預設初始化無法 呼叫隱含宣告的預設建構函式 |
若所有子物件均已初始化則允許 |
| CWG 616 | C++98 | 任何未初始化物件的左值到右值轉換 總是 UB |
允許不確定的 unsigned char |
| CWG 1787 | C++98 | 讀取不確定的 unsigned char 快取在暫存器中是 UB |
現已改為定義明確 |