web-dev-qa-db-ja.com

コンストラクタ内のこの奇妙なコロンメンバー( ":")構文は何ですか?

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

#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++の本には決してないでしょうか?

305
nils

これは、メンバーの初期化リストですgood C++ book でそれに関する情報を見つける必要があります。

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

FAQエントリからのポイントは、

他のすべての条件が等しい場合、割り当てではなく初期化リストを使用すると、コードの実行が高速になります。

190
James McNellis
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次の場合、メンバー初期化リストを使用します。

  • クラスには参照メンバーがいます
  • クラスに非静的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にはデフォルトのコンストラクターがないため、そのメンバーを初期化するにはメンバー初期化リストを使用する必要があります。

メンバー初期化リストを使用する際の注意事項:

クラスメンバー変数は常に、クラスで宣言されている順序で初期化されます。

これらはnot Member Initializer Listで指定された順序で初期化されます。
要するに、メンバー初期化リストは初期化の順序を決定しません。

上記を考えると、クラス定義で宣言されている順序と同じように、メンバーの初期化のメンバーの順序を常に維持することをお勧めします。これは、2つの順序が異なる場合、コンパイラは警告を発しませんが、比較的新しいユーザーがメンバーのInitializerリストを初期化の順序と混同し、それに依存するコードを記述する可能性があるためです。

293
Alok Save

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

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

効率性がすべてです。

14
Josh

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

12

これは不明瞭ではなく、 C++初期化リストの構文 です

基本的に、あなたの場合、x_xで、y_yで、z_zで初期化されます。

9
wkl

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

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

あなたの場合、データメンバのタイプはintです。 intはクラス型ではないため、コンストラクタはありません。タイプintの場合、この構文は単に「barの値でnumを初期化する」ことを意味し、それだけです。 intはクラス型ではないため、コンストラクターを使用できないため、コンストラクターは含まれません。

8
AnT

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

7
Mark Ransom

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

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

6
nos

これは、コンストラクターの初期化リストです。デフォルトでxyおよびzを構築し、パラメーターで受け取った値を割り当てる代わりに、これらのメンバーはすぐにそれらの値で初期化されます。これはfloatsにとってそれほど便利ではないように思えるかもしれませんが、構築に費用がかかるカスタムクラスを使用すると、時間を大幅に節約できます。

5
suszterpatt

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

5
Aric TenEyck

別の「利点」があります

メンバー変数型がnull初期化をサポートしていない場合、またはその参照(null初期化できない)の場合、初期化リストを提供する以外に選択肢はありません

5
pm100

このスレッドではまだ言及されていません:C++ 11以降、メンバー初期化子リストはリスト初期化(別名「均一な初期化」、「ブレース初期化」)を使用できます。

Foo(int num): bar{num} {}

他のコンテキストのリスト初期化と同じセマンティクスを持ちます。

1
M.M