初期化子
初期化子は変数の初期値を指定します。 変数は次のコンテキストで初期化できます。
変数の定義内:
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 など) に初期化されます。
文字変数は '\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 キーワードの後に空の丸かっこまたは中かっこを使用して初期化される。
値の初期化では次の処理が実行されます。
クラスに少なくとも 1 つのパブリック コンストラクターがある場合は、既定のコンストラクターが呼び出されます。
共用体以外のクラスでコンストラクターが宣言されていない場合、オブジェクトがゼロに初期化され、既定のコンストラクターが呼び出されます。
配列の場合、すべての要素の値が初期化されます。
それ以外の場合は、変数がゼロに初期化されます。
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 を使用して初期化される。
コンストラクターで、基底クラスと非静的メンバーが初期化子リストを使用して初期化される。
ラムダ式のキャプチャされた変数のコピーで。
直接の初期化の例を次に示します。
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 キーワードを使用して初期化される。
オブジェクトが関数から返される。
引数が関数に渡される。
直接の初期化の引数の 1 つに対して。
非静的データ メンバーの初期化子内で。
コンストラクターの初期化子リスト内で。
リストの初期化の例:
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' } };
}
集約の初期化
集約の初期化は、リストの初期化の一形態であり、次のような配列またはクラス型 (多くの場合は構造体や共用体) に使用されます。
プライベートまたはプロテクト メンバーでない。
明示的に既定化または削除したコンストラクターを除いて、ユーザー定義のコンストラクターがない。
基底クラスを持たない。
仮想メンバー関数がない。
非静的メンバーの中かっこまたは等号の初期化子がない。
集約の初期化子は、中かっこで囲まれた初期化子リストで構成されます。等号は使用してもしなくてもかまいません。
#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 でのように、ゼロに初期化されます。
共用体と構造体の初期化
共用体にコンストラクターがない場合は、値を使用して (または共用体の別のインスタンスを使用して) 共用体を初期化できます。 値は最初の非静的フィールドの初期化に使用されます。 この点は構造体の初期化と異なります。構造体の初期化では、初期化子の最初の値を使用して最初のフィールドを初期化し、2 番目の値を使用して 2 番目のフィールドを初期化するというように続きます。 この例では、共用体と構造体の初期化を比較します。
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 として宣言されていない場合にのみ初期化子を含めることができます。 詳細については、「外部」を参照してください。