396

最近、次のような例を見ました。

#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++ の本には見当たらない、このような難解な言語機能は他にありますか?

4

13 に答える 13

367
Foo(int num): bar(num)    

このコンストラクトは、C++ ではメンバー初期化リストと呼ばれます。

簡単に言うと、メンバーを値に初期化します。barnum


コンストラクタ内の初期化と代入の違いは何ですか?

メンバーの初期化:

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 つの順序が異なる場合にコンパイラが警告しないためですが、比較的新しいユーザーは、初期化の順序としてメンバ Initializer リストを混同し、それに依存するコードを記述する可能性があります。

于 2011-12-15T16:29:15.823 に答える
245

メンバーの初期化リストです。これに関する情報は、優れた C++ の本に記載されているはずです。

ほとんどの場合、メンバー初期化リスト内のすべてのメンバー オブジェクトを初期化する必要があります(ただし、FAQ エントリの最後にリストされている例外に注意してください)。

FAQ エントリからの要点は、次のとおりです。

他のすべての条件が同じであれば、代入ではなく初期化リストを使用すると、コードがより高速に実行されます。

于 2009-11-10T23:31:41.240 に答える
17

これがコンストラクターの初期化です。これは、デフォルトのコンストラクターが呼び出されないようにするため、クラスコンストラクターのメンバーを初期化する正しい方法です。

次の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

効率がすべてです。

于 2009-11-10T23:34:18.007 に答える
16

これは初期化リストと呼ばれます。これは、クラスメンバーを初期化する方法です。コンストラクターの本体のメンバーに単に新しい値を割り当てる代わりにこれを使用することには利点がありますが、定数または参照であるクラスメンバーがある場合は、それらを初期化する必要があります。

于 2009-11-10T23:35:42.047 に答える
9

これはあいまいではありません。C++ 初期化リストの構文です。

基本的に、あなたの場合、 、 with 、withでx初期化されます。_xy_yz_z

于 2010-12-11T19:56:51.610 に答える
8

もう1つは、観察する構文が「コンストラクタ初期化子リスト」と呼ばれることをすでに説明しています。この構文を使用すると、クラスの基本サブオブジェクトとメンバーサブオブジェクトをカスタム初期化できます(デフォルトで初期化するか、初期化しないままにするのとは対照的です)。

あなたが言ったように、「コンストラクター呼び出しのように見える」構文は、必ずしもコンストラクター呼び出しではないことに注意してください。C ++言語では、構文は初期化構文()の1つの標準形式にすぎません。タイプごとに解釈が異なります。ユーザー定義のコンストラクターを持つクラスタイプの場合、それは1つのことを意味し(実際にはコンストラクター呼び出しです)、ユーザー定義のコンストラクターのないクラスタイプの場合、それは別のこと(空の場合は値の初期化と呼ばれます)を意味し、非クラスタイプの場合は再びそれを意味します何か違うことを意味します(非クラス型にはコンストラクターがないため)。()

あなたの場合、データメンバーのタイプはintです。intはクラス型ではないため、コンストラクターはありません。タイプの場合int、この構文は単に「barの値で初期化する」ことを意味し、numそれだけです。これは、直接、コンストラクターが関与しないのと同じように行われますint。これも、クラスタイプではないため、コンストラクターを持つことができないためです。

于 2009-11-11T00:06:50.363 に答える
6

どうすればこれを見逃すことができるかわかりません、それはかなり基本的です。これは、メンバー変数または基本クラスコンストラクターを初期化するための構文です。これは、クラスオブジェクトだけでなくプレーンな古いデータ型でも機能します。

于 2009-11-10T23:32:54.967 に答える
6

これは初期化リストです。コンストラクタ本体が実行される前にメンバーを初期化します。検討

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' を使用します。

于 2009-11-10T23:42:06.030 に答える
5

あなたは正しいです、これは確かにメンバー変数を初期化する方法です。これが初期化であることを明確に表現する以外に、これに多くの利点があるかどうかはわかりません。コード内に「bar=num」があると、移動、削除、または誤解がはるかに簡単になる可能性があります。

于 2009-11-10T23:32:53.597 に答える
5

別の「メリット」がある

メンバー変数の型が null 初期化をサポートしていない場合、または参照 (null 初期化できない) の場合は、初期化リストを指定するしかありません。

于 2009-11-10T23:46:41.030 に答える
5

コンストラクタの初期化リストです。デフォルトで を構築しx、パラメータで受け取った値を割り当てる代わりに、これらのメンバーはすぐにそれらの値で初期化されます。これは s にとってはあまり役に立たないように見えるかもしれませんが、構築するのにコストがかかるカスタム クラスを使用する場合、かなりの時間を節約できます。yzfloat

于 2010-12-11T19:58:10.473 に答える