この特定のケースでは、メンバー初期化子リストの使用とコンストラクターでの値の割り当てに違いはありますか?


90

内部的に、生成されたコードについて、次の間に本当に違いがありますか?

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

そして

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

ありがとう...

回答:


60

これらの値がプリミティブ型であると仮定すると、違いはありません。違いはありません。初期化リストは、デフォルトの初期化に続いて割り当てを使用する代わりに、オブジェクトをメンバーとして持つ場合にのみ違いを生じさせます。初期化リストを使用すると、オブジェクトをその最終値に初期化できます。これは実際には著しく速くなります。


17
リチャードが言ったように、それらの値がプリミティブ型とconstである場合に違いが生じます。初期化リストは、値をconstメンバーに割り当てる唯一の方法です。
thbusch 2011年

11
変数が参照または定数でない場合にのみ、説明どおりに機能します。それらが定数または参照の場合、初期化リストを使用しないとコンパイルできません。
stefanB 2011年

77

初期化リストを使用して、定数メンバー、参照、および基本クラスを初期化する必要があります

コメントで述べられているように、定数メンバー、参照を初期化し、パラメーターを基本クラスコンストラクターに渡す必要がある場合は、初期化リストを使用する必要があります。

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

例(網羅的ではない)クラス/構造体には参照が含まれています。

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

そして、パラメーターを必要とする基本クラスを初期化する例(デフォルトのコンストラクターなしなど):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

4
そして、もし_capacity_dataおよび_lenアクセスデフォルトコンストラクタなしでクラスの型を持っていますか?
CBベイリー

2
利用可能なコンストラクタを呼び出します。さらに値を設定する必要がある場合は、コンストラクタの本体から呼び出します。ここでの違いはconst、コンストラクターの本体でメンバーを初期化できないことです。初期化リストを使用する必要があります-非constメンバーは、初期化リストまたはコンストラクターの本体で初期化できます。
stefanB

@stefan:コンストラクターを呼び出すことはできません。デフォルトコンストラクターのないクラスは、constメンバーと同様に、初期化子リストで初期化する必要があります。
GManNickG、2011年

2
また、初期化リストで参照を初期化する必要があります
Andriy Tylychko

2
@stefanB:申し訳ありませんが、実際に理解しても、理解できないことを暗に示しています。あなたの答えでは、初期化リストでベースまたはメンバーに名前を付ける必要がある場合のみを述べましたが、初期化リストでの初期化とコンストラクター本体での割り当ての概念的な違いが実際に何であるかを説明していませんから来た可能性があります。
CBベイリー

18

はい。最初のケースでは、宣言することができ_capacity_dataおよび_len定数として:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

constこれらのインスタンス変数の実行時に値を計算する際に、これらのインスタンス変数が適切であることを確認したい場合は、次のように重要です。

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

constインスタンスはイニシャライザリストで初期化する必要あります。そうでない場合、基になる型は、パラメータなしのパブリックコンストラクタを提供する必要があります(プリミティブ型はこれを行います)。


2
同じことがリファレンスにも当てはまります。クラスに参照メンバーがある場合は、初期化リストで初期化する必要があります。
Mark Ransom、2011年

7

このリンクhttp://www.cplusplus.com/forum/articles/17820/は、特にC ++を初めて使用する人にとって、すばらしい説明になると思います。

初期化リストがより効率的である理由は、コンストラクター本体内では割り当てのみが行われ、初期化は行われないためです。したがって、非組み込み型を扱う場合、コンストラクターの本体が入力される前に、そのオブジェクトのデフォルトコンストラクターがすでに呼び出されています。コンストラクタ本体の中で、そのオブジェクトに値を割り当てています。

実際には、これはデフォルトのコンストラクターへの呼び出しと、それに続くコピー代入演算子の呼び出しです。初期化リストを使用すると、コピーコンストラクターを直接呼び出すことができます。これにより、かなり高速になる場合があります(初期化リストはコンストラクターの本体の前にあることを思い出してください)。


5

デフォルトのコンストラクターが利用できないクラス型のメンバーがある場合は、初期化がクラスを構築する唯一の方法であることを付け加えます。


3

大きな違いは、割り当てによって親クラスのメンバーを初期化できることです。初期化子は、現在のクラススコープで宣言されたメンバーでのみ機能します。


2

関係するタイプによって異なります。違いは似ています

std::string a;
a = "hai";

そして

std::string a("hai");

2番目の形式は初期化リストです。つまり、型がコンストラクター引数を必要とする場合、またはコンストラクター引数を使用した方が効率的である場合に違いがあります。


1

実際の違いは、gccコンパイラーがマシンコードを生成してメモリーを配置する方法に要約されます。説明:

  • (フェーズ1)initボディ(initリストを含む)の前:コンパイラはクラスに必要なメモリを割り当てます。クラスはすでに生きています!
  • (フェーズ2)init本文:メモリが割り当てられているため、すべての割り当ては、すでに存在する/「初期化された」変数に対する操作を示します。

const型のメンバーを処理する方法は確かに他にもあります。しかし、生活を楽にするために、gccコンパイラの作成者はいくつかのルールを設定することにしました

  1. const型のメンバーは、init本文の前に初期化する必要があります。
  2. フェーズ1以降の書き込み操作は、非定数メンバーに対してのみ有効です。

1

基本クラスインスタンスと非静的メンバー変数を初期化する方法は1つしかなく、それは初期化子リストを使用しています。

コンストラクターの初期化子リストでベースまたは非静的メンバー変数を指定しない場合、そのメンバーまたはベースはデフォルトで初期化されます(メンバー/ベースが非PODクラスタイプまたは非PODクラスの配列の場合)タイプ)またはそれ以外の場合は初期化されないままにします。

コンストラクター本体に入ると、すべてのベースまたはメンバーが初期化されるか、初期化されないままになります(つまり、不定の値になります)。コンストラクター本体には、初期化方法に影響を与える機会はありません。

コンストラクター本体のメンバーに新しい値を割り当てることができる場合がありますが、constメンバーまたはクラスタイプのメンバーに割り当てることができなくなり、参照を再バインドすることはできません。

組み込み型および一部のユーザー定義型の場合、コンストラクター本体での割り当ては、初期化子リストの同じ値で初期化するのとまったく同じ効果がある場合があります。

イニシャライザリストのメンバーまたはベースの名前付けに失敗し、そのエンティティが参照であり、アクセス可能なユーザー宣言のデフォルトコンストラクターがないクラスタイプであり、const修飾され、PODタイプであるか、PODクラスタイプまたはPODクラスタイプの配列である場合含むconst修飾体を(直接的または間接的に)、プログラムが悪い形成されています。


0

イニシャライザリストを作成する場合は、すべて1つのステップで行います。初期化リストを記述しない場合は、宣言と値の割り当ての2つのステップを実行します。


0

コンストラクター内の初期化リストと初期化ステートメントには違いがあります。以下のコードを考えてみましょう:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

MyClassを使用すると、コンストラクターの最初のステートメントが実行される前に、すべてのメンバーが初期化されます。

ただし、MyClass2を使用すると、コンストラクターの最初のステートメントが実行されたときに、すべてのメンバーが初期化されません。

後者の場合、特定のメンバーが初期化される前に、誰かがコンストラクターにコードを追加したときに、回帰問題が発生する可能性があります。


0

ここで私が他の人がそれを参照するのを見なかった点があります:

class temp{
public:
   temp(int var);
};

tempクラスにはデフォルトのctorはありません。次のように別のクラスで使用する場合:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

コードがコンパイルされず、コンパイラーが初期化の方法を認識できないため、コードにはobjint値を受け取る明示的なctorしかないため、次のようにctorを変更する必要があります。

mainClass(int sth):obj(sth){}

したがって、それはconstと参照だけではありません!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.