名稱空間
變體
操作

預設初始化

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

這是物件在沒有初始化器的情況下構造時執行的初始化。

目錄

[編輯] 語法

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

[編輯] 解釋

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

1) 當一個具有自動、靜態或執行緒區域性儲存期的變數在沒有初始化器的情況下宣告時;
2) 當一個具有動態儲存期的物件透過沒有初始化器的new 表示式建立時;
3) 當基類或非靜態資料成員未在建構函式初始化列表中提及,並且該建構函式被呼叫時。

預設初始化的效果是:

  • 如果 T 是一個(可能是 cv 限定的)非 POD(直到 C++11) 類型別,則考慮建構函式並針對空引數列表進行過載決議。選擇的建構函式(它是預設建構函式之一)被呼叫以提供新物件的初始值;
  • 如果 T 是陣列型別,則陣列的每個元素都預設初始化;
  • 否則,不執行任何初始化(參見註釋)。

[編輯] const 物件的預設初始化

如果程式要求預設初始化一個 const 限定型別 T 的物件,則 T 應為const 預設可構造類型別或其陣列。

如果 T 的預設初始化會呼叫 T 的使用者提供建構函式(不是從基類繼承的)(自 C++11 起),或者如果

只有具有自動儲存持續時間的(可能是 cv 限定的)非 POD 類型別(或其陣列)在沒有初始化器的情況下被視為預設初始化。具有動態儲存持續時間的標量和 POD 型別被視為未初始化(自 C++11 起,這種情況被重新歸類為一種預設初始化)。

(C++11 前)
  • T 的每個直接非靜態資料成員 M 都是類型別 X(或其陣列),X 是 const 預設可構造的,並且
  • T 沒有直接變體成員,並且
(C++11 前)
  • T 的每個直接非變體非靜態資料成員 M 都有一個預設成員初始化器,或者如果 M 是類型別 X(或其陣列),X 是 const 預設可構造的,
  • 如果 T 是至少有一個非靜態資料成員的 union,則恰好一個變體成員具有預設成員初始化器,
  • 如果 T 不是 union,對於每個至少有一個非靜態資料成員的匿名 union 成員(如果有),恰好一個非靜態資料成員具有預設成員初始化器,並且
(C++11 起)

T 的每個可能構造的基類都是 const 預設可構造的。

[編輯] 不確定和錯誤的值

當為具有自動或動態儲存持續時間的物件分配儲存空間時,該物件具有一個不確定的值

如果未對物件執行初始化,則該物件將保留不確定的值,直到該值被替換。

(直到 C++26)

當為具有自動或動態儲存持續時間的物件分配儲存空間時,構成物件儲存的位元組具有以下初始值:

  • 如果物件具有動態儲存持續時間,或者與變數或函式引數相關聯,且其第一次宣告標記為 [[indeterminate]],則這些位元組具有不確定的值
  • 否則,這些位元組具有錯誤的值,其中每個值由實現獨立於程式狀態確定。

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

  • 如果值表示中的任何位具有不確定的值,則該物件具有不確定的值
  • 否則,如果值表示中的任何位具有錯誤的值,則該物件具有錯誤的值
(C++26 起)

如果評估產生不確定的值,則行為是未定義的。

如果評估產生錯誤的值,則行為是錯誤的

(C++26 起)

[編輯] 特殊情況

以下型別是未初始化友好型

(C++17 起)
  • unsigned char
  • char,如果其底層型別是 unsigned char

給定一個不確定或錯誤(自 C++26 起)的值 valuevalue未初始化結果值是:

  • 不確定的值,如果 value 也是不確定的值。
  • value,如果 value 是一個錯誤的值。
(C++26 起)

如果評估 eval 產生一個未初始化友好型別的不確定或錯誤(自 C++26 起)的值 value,則在以下情況下行為是明確定義的:

  • eval 是以下表達式和運算元之一的評估:
在這種情況下,操作的結果是 value 的未初始化結果值。
  • eval簡單賦值運算子的右運算元的評估,其左運算元是未初始化友好型別的左值。
在這種情況下,左運算元引用的物件的值被 value 的未初始化結果值替換。
  • eval 是初始化表示式的評估,用於初始化未初始化友好型別的物件。
  • 如果被初始化的物件不是 std::byte 型別,則 value 不能是 std::byte 型別。
(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++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 178 C++98 沒有值初始化;
空初始化器呼叫預設初始化
(儘管 new T() 也執行零初始化)
空初始化器呼叫
值初始化
CWG 253 C++98 const 物件的預設初始化不能
呼叫隱式宣告的預設建構函式
如果所有子物件都已初始化,則允許
CWG 616 C++98 任何未初始化物件的左值到右值轉換總是未定義行為
未定義
允許不確定的 unsigned char
CWG 1787 C++98 從不確定的 unsigned char 讀取
快取到暫存器中是未定義行為
已明確定義

[編輯] 另請參閱