最近、次のような例を見ました。
#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++の本には決してないでしょうか?
これは、メンバーの初期化リストです。 good C++ book でそれに関する情報を見つける必要があります。
ほとんどの場合、メンバー初期化リスト内のすべてのメンバーオブジェクトを初期化する必要があります (ただし、FAQエントリの最後にリストされている例外に注意してください)。
FAQエントリからのポイントは、
他のすべての条件が等しい場合、割り当てではなく初期化リストを使用すると、コードの実行が高速になります。
Foo(int num): bar(num)
このコンストラクトは、C++ではMember Initializer Listと呼ばれます。
簡単に言えば、それは初期化メンバーbar
を値num
に。
メンバーの初期化:
Foo(int num): bar(num) {};
メンバーの割り当て:
Foo(int num)
{
bar = num;
}
メンバー初期化子リストを使用してメンバーを初期化することと、コンストラクター本体内で値を割り当てることとの間には大きな違いがあります。
メンバー初期化子リストを介してinitializeフィールドを使用すると、コンストラクターが1回呼び出され、1回の操作でオブジェクトが構築および初期化されます。
assignmentを使用すると、フィールドは最初にデフォルトコンストラクターで初期化され、次に実際の値で(割り当て演算子を介して)再割り当てされます。
ご覧のように、後者では作成と割り当てのオーバーヘッドが追加されます。これは、ユーザー定義のクラスではかなりの量になる可能性があります。
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クラスメンバーの場合、実際的なオーバーヘッドはありません。
have(rather forced)to次の場合、メンバー初期化リストを使用します。
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
にはデフォルトのコンストラクターがないため、そのメンバーを初期化するにはメンバー初期化リストを使用する必要があります。クラスメンバー変数は常に、クラスで宣言されている順序で初期化されます。
これらはnot Member Initializer Listで指定された順序で初期化されます。
要するに、メンバー初期化リストは初期化の順序を決定しません。
上記を考えると、クラス定義で宣言されている順序と同じように、メンバーの初期化のメンバーの順序を常に維持することをお勧めします。これは、2つの順序が異なる場合、コンパイラは警告を発しませんが、比較的新しいユーザーがメンバーのInitializerリストを初期化の順序と混同し、それに依存するコードを記述する可能性があるためです。
それはコンストラクタの初期化です。デフォルトのコンストラクターが呼び出されるのを防ぐため、クラスコンストラクターのメンバーを初期化する正しい方法です。
次の2つの例を検討してください。
// Example 1
Foo(Bar b)
{
bar = b;
}
// Example 2
Foo(Bar b)
: bar(b)
{
}
例1では:
Bar bar(); // default constructor
bar = b; // assignment
例2では:
Bar bar(b) // copy constructor
効率性がすべてです。
これは初期化リストと呼ばれます。クラスメンバーを初期化する別の方法です。単にコンストラクタの本体のメンバーに新しい値を割り当てる代わりにこれを使用する利点がありますが、constantsまたはreferences彼らmust初期化する必要があります。
これは不明瞭ではなく、 C++初期化リストの構文 です
基本的に、あなたの場合、x
は_x
で、y
は_y
で、z
は_z
で初期化されます。
もう1つは、観察する構文が「コンストラクター初期化リスト」と呼ばれることを既に説明しています。この構文を使用すると、クラスのベースサブオブジェクトとメンバーサブオブジェクトをカスタム初期化できます(これらをデフォルトの初期化または未初期化のままにするのではなく)。
あなたが言ったように、「コンストラクター呼び出しのように見える」構文は、必ずしもコンストラクター呼び出しではないことに注意してください。 C++言語では、()
構文は、初期化構文の1つの標準形式にすぎません。タイプごとに解釈が異なります。ユーザー定義のコンストラクターを持つクラスタイプの場合は、1つのこと(実際にはコンストラクター呼び出し)を意味し、ユーザー定義のコンストラクターのないクラスタイプの場合は、別のことを意味します(いわゆる 値の初期化 )空の()
)および非クラス型の場合、それはまた別の何かを意味します(非クラス型にはコンストラクタがないため) )。
あなたの場合、データメンバのタイプはint
です。 int
はクラス型ではないため、コンストラクタはありません。タイプint
の場合、この構文は単に「bar
の値でnum
を初期化する」ことを意味し、それだけです。 int
はクラス型ではないため、コンストラクターを使用できないため、コンストラクターは含まれません。
どうやってこれを見逃すことができるかわかりません、それはかなり基本的です。これは、メンバー変数または基本クラスコンストラクターを初期化するための構文です。クラスオブジェクトだけでなく、プレーンな古いデータ型でも機能します。
これは初期化リストです。コンストラクターの本体が実行される前にメンバーを初期化します。検討する
class Foo {
public:
string str;
Foo(string &p)
{
str = p;
};
};
対
class Foo {
public:
string str;
Foo(string &p): str(p) {};
};
最初の例では、strは引数なしのコンストラクターによって初期化されます
string();
fooコンストラクターの本体の前。 fooコンストラクター内では、
string& operator=( const string& s );
str = pを実行すると、 'str'で呼び出されます。
2番目の例では、strはコンストラクターを呼び出すことで直接初期化されます
string( const string& s );
引数として「p」を使用します。
これは、コンストラクターの初期化リストです。デフォルトでx
、y
およびz
を構築し、パラメーターで受け取った値を割り当てる代わりに、これらのメンバーはすぐにそれらの値で初期化されます。これはfloat
sにとってそれほど便利ではないように思えるかもしれませんが、構築に費用がかかるカスタムクラスを使用すると、時間を大幅に節約できます。
あなたは正しい、これは確かにメンバー変数を初期化する方法です。これが初期化であることを明確に表現する以外に、これに多くの利点があるかどうかはわかりません。コード内に「bar = num」を含めると、移動、削除、または誤解されやすくなります。
別の「利点」があります
メンバー変数型がnull初期化をサポートしていない場合、またはその参照(null初期化できない)の場合、初期化リストを提供する以外に選択肢はありません
このスレッドではまだ言及されていません:C++ 11以降、メンバー初期化子リストはリスト初期化(別名「均一な初期化」、「ブレース初期化」)を使用できます。
Foo(int num): bar{num} {}
他のコンテキストのリスト初期化と同じセマンティクスを持ちます。