最近、次のような例を目にしました。
#include <iostream>
class Foo {
public:
int bar;
Foo(int num): bar(num) {};
};
int main(void) {
std::cout << Foo(42).bar << std::endl;
return 0;
}
この奇妙な: bar(num)
意味は何でしょうか? どうやらデータ メンバーを初期化しているようですが、この構文は初めて見ました。 関数/コンストラクター呼び出しのように見えますが、 用ですint
。私には意味がわかりません。
普通の C++ の本では決して見つけられないような、このような難解な言語機能は他にありますか?
ベストアンサー1
Foo(int num): bar(num)
この構造は、C++ ではメンバー初期化リストと呼ばれます。
簡単に言えば、メンバーを値に初期化します。bar
num
コンストラクター内の初期化と割り当ての違いは何ですか?
メンバーの初期化:
Foo(int num): bar(num) {};
メンバーの割り当て:
Foo(int num)
{
bar = num;
}
メンバー初期化リストを使用してメンバーを初期化することと、コンストラクター本体内でメンバーに値を割り当てることとの間には大きな違いがあります。
メンバー初期化リストを介してフィールドを初期化すると、コンストラクターが 1 回呼び出され、オブジェクトが 1 回の操作で構築および初期化されます。
代入を使用する場合、フィールドは最初にデフォルトのコンストラクターで初期化され、その後、実際の値で(代入演算子を介して)再代入されます。
ご覧のとおり、後者では作成と割り当ての追加のオーバーヘッドがあり、これはユーザー定義クラスではかなりのものになる可能性があります。
Cost of Member Initialization = Object Construction
Cost of Member Assignment = Object Construction + Assignment
後者は実際には次のものと同等です:
Foo(int num) : bar() {bar = num;}
前者は次のものと同等です:
Foo(int num): bar(num){}
組み込み (コード例) または POD クラス メンバーの場合、実質的なオーバーヘッドはありません。
メンバー初期化リストをいつ使用する必要がありますか?
以下の場合には、メンバー初期化リストを使用する必要があります(むしろ強制されます) 。
- クラスに参照メンバーがあります
- クラスに非静的constメンバーがあるか、
- クラスメンバーにデフォルトコンストラクタがないか、
- 基本クラスのメンバーの初期化または
- コンストラクタのパラメータ名がデータメンバーと同じ場合(これは必ずしも必須ではありません)
コード例:
class MyClass {
public:
// Reference member, has to be Initialized in Member Initializer List
int &i;
int b;
// Non static const member, must be Initialized in Member Initializer List
const int k;
// Constructor’s parameter name b is same as class data member
// Other way is to use this->b to refer to data member
MyClass(int a, int b, int c) : i(a), b(b), k(c) {
// Without Member Initializer
// this->b = b;
}
};
class MyClass2 : public MyClass {
public:
int p;
int q;
MyClass2(int x, int y, int z, int l, int m) : MyClass(x, y, z), p(l), q(m) {}
};
int main() {
int x = 10;
int y = 20;
int z = 30;
MyClass obj(x, y, z);
int l = 40;
int m = 50;
MyClass2 obj2(x, y, z, l, m);
return 0;
}
MyClass2
デフォルト コンストラクターがないため、メンバー初期化リストを通じて初期化する必要があります。- 基本クラスには
MyClass
デフォルトのコンストラクターがないため、メンバーを初期化するにはメンバー初期化リストを使用する必要があります。
メンバー初期化リストを使用する際に注意すべき重要な点:
クラス メンバー変数は常に、クラス内で宣言された順序で初期化されます。
メンバー初期化リストに指定された順序で初期化されるわけではありません。つまり、
メンバー初期化リストは初期化の順序を決定しません。
上記を踏まえると、メンバー初期化のメンバーの順序は、クラス定義で宣言された順序と同じにしておくことが常に良い方法です。これは、2 つの順序が異なっていてもコンパイラは警告を出さないものの、比較的新しいユーザーはメンバー初期化子リストを初期化の順序と混同し、それに依存するコードを書く可能性があるためです。