初始設定式
初始設定式指定變數的初始值。 您可以初始化下列內容中的變數:
變數的定義中:
int i = 3; Point p1{ 1, 2 };
函式的其中一個參數:
set_point(Point{ 5, 6 });
函式的傳回值:
Point get_new_point(int x, int y) { return { x, y }; } Point get_new_point(int x, int y) { return Point{ x, y }; }
初始設定式可能會有下列格式:
以括號刮住的運算式 (或逗號分隔的運算式清單):
Point p1(1, 2);
運算式後面接著等號:
string s = "hello";
以大括號括住的初始設定式清單。 該清單可能是空的,也可能包含一組清單:
struct Point{ int x; int y; }; class PointConsumer{ public: void set_point(Point p){}; void set_points(initializer_list<Point> my_list){}; }; int main() { PointConsumer pc{}; pc.set_point({}); pc.set_point({ 3, 4 }); pc.set_points({ { 3, 4 }, { 5, 6 } }); }
初始化的類型
初始化分為數種類型,可能會在程式執行的不同時期發生。 不同類型的初始化不會互斥,例如清單初始化可以觸發值初始化,而在其他情況下,則可觸發彙總初始化。
零初始化
零初始化是將變數設為隱含轉換為類型的零值:
數值變數初始化為 0 (或 0.0、0.0000000000 等)。
Char 變數初始化為 ‘\0’。
指標初始化為 nullptr。
POD 類別、結構和等位的成員一定會初始化為零值。
零初始化在不同時期執行:
在程式啟動時,對象是具有靜態期間的所有具名變數。 之後可以再次初始化這些變數。
在初始化值期間,對象是純量類型和使用空括號初始化的 POD 類別類型。
只初始化其成員子集的陣列。
以下舉例說明零初始化:
struct my_struct{
int i;
char c;
};
int i0; // zero-initialized to 0
int main() {
static float f1; // zero-initialized to 0.000000000
double d{}; // zero-initialized to 0.00000000000000000
int* ptr{}; // initialized to nullptr
char s_array[3]{'a', 'b'}; // the third char is initialized to '\0'
int int_array[5] = { 8, 9, 10 }; // the fourth and fifth ints are initialized to 0
my_struct a_struct{}; // i = 0, c = '\0'
}
預設的初始化
類別、結構和等位的預設初始化是使用預設建構函式的初始化。 不包含初始運算式或使用 new 關鍵字即可呼叫預設建構函式:
MyClass mc1;
MyClass* mc3 = new MyClass;
如果類別、結構或等位沒有預設建構函式,則編譯器會發出錯誤。
定義純量變數時若未使用初始運算式,預設會初始化。 這些變數都具有不定值。
int i1;
float f;
char c;
定義陣列時若未使用初始化運算式,預設會初始化。 預設初始化陣列時,預設會初始其成員,且其成員具有下列不定值:
int int_arr[3];
如果陣列成員沒有預設建構函式,則編譯器會發出錯誤。
常數變數的預設初始化
宣告常數變數時必須搭配使用初始設定式。 如果常數變數是純量類型,會引發編譯器錯誤,若是具有預設建構函式的類別類型,則會引發警告:
class MyClass{};
int main() {
//const int i2; // compiler error C2734: const object must be initialized if not extern
//const char c2; // same error
const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}
靜態變數的預設初始化
未使用初始設定式宣告的靜態變數會初始化為 0 (隱含轉換為類型):
class MyClass {
private:
int m_int;
char m_char;
};
int main() {
static int int1; // 0
static char char1; // '\0'
static bool bool1; // false
static MyClass mc1; // {0, '\0'}
}
如需全域靜態物件初始化的詳細資訊,請參閱其他啟動考量。
值初始化
下列情況下會發生值初始化:
使用空括號初始設定初始化具名的值。
使用空括號或大括號初始化匿名暫存物件。
使用 new 關鍵字加上空括號或大括號初始化物件。
值初始化會執行下列作業:
對於至少有一個公用建構函式的類別,會呼叫預設建構函式。
對於沒有宣告建構函式的非等位類別,會將該物件初始化為零並呼叫預設建構函式。
對於陣列,會將每個元素初始化為值。
在所有其他情況下,會將變數初始化為零。
class BaseClass {
private:
int m_int;
};
int main() {
BaseClass bc{}; // class is initialized
BaseClass* bc2 = new BaseClass(); // class is initialized, m_int value is 0
int int_arr[3]{}; // value of all members is 0
int a{}; // value of a is 0
double b{}; // value of b is 0.00000000000000000
}
複製初始化
複製初始化是使用不同的物件初始化同一個物件。 下列情況會發生此類初始化:
使用等號初始化變數。
將引數傳遞至函式。
從函式傳回物件。
擲回或攔截例外狀況。
使用等號初始化非靜態資料成員。
複製初始化會在彙總初始化期間初始化類別、結構和等位成員。 如需範例,請參閱彙總初始化。
下列程式碼示範複製初始化:
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(int myInt) {}
void set_int(int myInt) { m_int = myInt; }
int get_int() const { return m_int; }
private:
int m_int = 7; // copy initialization of m_int
};
class MyException : public exception{};
int main() {
int i = 5; // copy initialization of i
MyClass mc1{ i };
MyClass mc2 = mc1; // copy initialization of mc2 from mc1
MyClass mc1.set_int(i); // copy initialization of parameter from i
int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()
try{
throw MyException();
}
catch (MyException ex){ // copy initialization of ex
cout << ex.what();
}
}
複製初始化無法叫用明確建構函式:
vector<int> v = 10; // the constructor is explicit; compiler error C2440: cannot convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error
在某些情況下,若刪除或無法存取類別的複製建構函式,複製初始化會引發編譯器錯誤。 如需詳細資訊,請參閱明確初始化。
直接初始化
直接初始化使用 (非空白) 括號或大括號。 直接初始化可以叫用明確建構函式,這點與複製初始化不同。 下列情況會發生此類初始化:
使用非空白括號或大括號初始化變數。
使用 new 關鍵字加上非空白括號或大括號初始化變數。
使用 static_cast 初始化變數。
在建構函式中,會使用初始設定式清單初始化基底類別和非靜態成員。
在 Lambda 運算式之攔截變數的複本中。
下列範例說明直接初始化:
class BaseClass{
public:
BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
int m_int;
};
class DerivedClass : public BaseClass{
public:
// BaseClass and m_char are direct initialized
DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
char m_char;
};
int main(){
BaseClass bc1(5);
DerivedClass dc1{ 1, 'c' };
BaseClass* bc2 = new BaseClass(7);
BaseClass bc3 = static_cast<BaseClass>(dc1);
int a = 1;
function<int()> func = [a](){ return a + 1; }; // a is direct initialized
int n = func();
}
清單初始化
使用以大括號括住的初始設定式清單初始化變數時,就會發生清單初始化。 下列情況可使用以大括號括住的初始設定式清單:
初始化變數。
使用 new 關鍵字初始化類別。
從函式傳回物件。
將引數傳遞至函式。
直接初始化中的其中一個引數。
非靜態資料成員初始設定式中。
建構函式初始設定式清單中。
清單初始化範例:
class MyClass {
public:
MyClass(int myInt, char myChar) {}
private:
int m_int[]{ 3 };
char m_char;
};
class MyClassConsumer{
public:
void set_class(MyClass c) {}
MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
int my_int;
char my_char;
MyClass my_class;
};
int main() {
MyClass mc1{ 1, 'a' };
MyClass* mc2 = new MyClass{ 2, 'b' };
MyClass mc3 = { 3, 'c' };
MyClassConsumer mcc;
mcc.set_class(MyClass{ 3, 'c' });
mcc.set_class({ 4, 'd' });
MyStruct ms1{ 1, 'a', { 2, 'b' } };
}
彙總初始化
彙總初始化是陣列或類別類型 (通常是結構或等位) 的清單初始化表單,這些陣列或類別類型具有:
非 private 成員或 protected 成員。
沒有使用者提供的建構函式 (明確預設或刪除的建構函式除外)。
沒有基底類別。
沒有虛擬成員函式。
非靜態成員沒有大括號或等號初始設定式。
彙總初始設定式包含以大括號括住的初始化清單 (包含或不含等號):
#include <iostream>
using namespace std;
struct MyAggregate{
int myInt;
char myChar;
};
int main() {
MyAggregate agg1{ 1, 'c' };
cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;
int myArr1[]{ 1, 2, 3, 4 };
int myArr2[3] = { 5, 6, 7 };
int myArr3[5] = { 8, 9, 10 };
cout << "myArr1: ";
for (int i : myArr1){
cout << i << " ";
}
cout << endl;
cout << "myArr3: ";
for (auto const &i : myArr3) {
cout << i << " ";
}
cout << endl;
}
輸出如下:
agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0
重要
彙總初始化期間宣告但未明確初始化的陣列成員會初始化為零,像在 myArr3 中一樣。
初始化等位和結構
如果等位沒有建構函式,可以使用值 (或使用等位的另一個執行個體) 將它初始化。 該值用於初始化第一個非靜態欄位。 此初始化與結構初始化不同,後者會使用初始化設定式中的第一個值初始化第一個欄位、第二個值初始化第二個欄位,依此類推。 比較此範例中的等位初始化和結構初始化:
struct MyStruct {
int myInt;
char myChar;
};
union MyUnion {
int my_int;
char my_char;
bool my_bool;
MyStruct my_struct;
};
int main() {
MyUnion mu1{ 'a' }; // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
MyUnion mu2{ 1 }; // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
MyUnion mu3{}; // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
MyUnion mu4 = mu3; // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
//MyUnion mu5{ 1, 'a', true }; // compiler error: C2078: too many initializers
//MyUnion mu6 = 'a'; // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
//MyUnion mu7 = 1; // compiler error: C2440: cannot convert from 'int' to 'MyUnion'
MyStruct ms1{ 'a' }; // myInt = 97, myChar = '\0'
MyStruct ms2{ 1 }; // myInt = 1, myChar = '\0'
MyStruct ms3{}; // myInt = 0, myChar = '\0'
MyStruct ms4{1, 'a'}; // myInt = 1, myChar = 'a'
MyStruct ms5 = { 2, 'b' }; // myInt = 2, myChar = 'b'
}
初始化包含彙總的彙總
彙總類型可以包含其他彙總類型,例如陣列、陣列結構,依此類推。 這些類型均使用巢狀大括號進行初始化:
struct MyStruct {
int myInt;
char myChar;
};
int main() {
int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
int intArr3[2][2] = {1, 2, 3, 4};
MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}
參考初始化
如需參考初始化的詳細資訊,請參閱初始化參考。
外部變數的初始化
自動、暫存器、靜態和外部變數的宣告可以包含初始設定式。 不過,只有在不將變數宣告為 extern 時,外部變數宣告才能包含初始設定式。 如需詳細資訊,請參閱外部。