命名空間
變體
動作

預設初始化

出自 cppreference.com
< cpp‎ | language
 
 
C++ 語言
一般主題
流程控制
條件執行陳述式
if
疊代陳述式 (迴圈)
for
範圍 for (C++11)
跳躍陳述式
函式
函式宣告
Lambda 函式運算式
inline 指定符
動態例外規範 (直到 C++17*)
noexcept 指定符 (C++11)
例外
命名空間
型別
指定符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期指定符
初始化
 
 

這是當物件在建構時沒有提供初始器(initializer)所執行的初始化。

目錄

[編輯] 語法

T 物件 ; (1)
new T (2)

[編輯] 說明

預設初始化會在下列三種情況下執行:

1) 當宣告具有自動、靜態或執行緒區域 儲存期 (storage duration) 的變數且沒有提供初始器時;
2) 當透過沒有初始器的 new 表達式建立具有動態儲存期的物件時;
3) 當基底類別或非靜態資料成員未在 建構函式初始化列表中提及,且呼叫了該建構函式時。

預設初始化的效果如下:

  • 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)
  • T 的每個直接非靜態資料成員 M 皆為類別型別 X(或其陣列),且 X 為 const-預設可建構,且
  • T 沒有直接的 變體成員 (variant members),且
(直到 C++11)
  • T 的每個直接非變體非靜態資料成員 M 都有 預設成員初始器,若 M 為類別型別 X(或其陣列),則 X 為 const-預設可建構,
  • T 為具有至少一個非靜態資料成員的聯集 (union),則恰好有一個 變體成員 具有預設成員初始器,
  • T 不是聯集,則對於每個具有至少一個非靜態資料成員的匿名聯集成員(若有),恰好有一個非靜態資料成員具有預設成員初始器,且
(C++11 起)

T 的每個 潛在建構 (potentially constructed) 基底類別均為 const-預設可建構。

[編輯] 不確定值與錯誤值

當獲得具有自動或動態儲存期之物件的儲存空間時,該物件具有「不確定值」(indeterminate value)。

若物件未進行任何初始化,該物件將保持不確定值,直到該值被替換為止。

(直到 C++26)

當獲得具有自動或動態儲存期之物件的儲存空間時,組成該物件儲存空間的位元組具有下列初始值:

  • 若該物件具有動態儲存期,或是與其首次宣告被標記為 [[indeterminate]] 的變數或 函式參數 相關聯的物件,則該位元組具有「不確定值」。
  • 否則,該位元組具有「錯誤值」(erroneous values),其中每個值皆由實作獨立於程式狀態而決定。

若未對物件(包含其 子物件)執行初始化,則此類位元組將保留其初始值直到被替換。

  • 值表示 (value representation) 中的任何位元具有不確定值,則該物件具有「不確定值」。
  • 否則,若值表示中的任何位元具有錯誤值,則該物件具有「錯誤值」。
(C++26 起)

若求值產生不確定值,則其行為為 未定義 (undefined)

若求值產生錯誤值,則其行為為 錯誤的 (erroneous)

(C++26 起)

[編輯] 特殊情況

下列型別被稱為「未初始化友善」(uninitialized-friendly):

(自 C++17 起)
  • unsigned char
  • char,若其底層型別為 unsigned char

給定一個不確定或錯誤(C++26 起)valuevalue 的「未初始化結果值」(uninitialized result value) 為:

  • value 亦為不確定值,則為不確定值。
  • value 為錯誤值,則為 value
(C++26 起)

若求值 eval 產生了未初始化友善型別的不確定或錯誤(C++26 起)value,則在下列情況下行為良好定義:

  • eval 是下列表達式與運算元的求值:
在此情況下,運算的結果為 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
現已改為定義明確

[編輯] 參見

English Deutsch 日本語 中文(简体) 中文(繁體)