web-dev-qa-db-ja.com

宣言時の新しいC ++ 11メンバー初期化機能により、初期化リストが廃止されましたか?

C++ 11では、ヘッダー宣言でクラスメンバーを初期化することができます。

class aClass
{
    private:
        int mInt{100};
    public:
         aClass();
        ~aClass();
};

だから私は少し混乱しています。従来、コンストラクターの初期化リストはメンバーの初期化に使用されていました。

aClass::aClass()
: mInt(100)
{
    ...
}

宣言時の新しいC++ 11メンバー初期化機能により、初期化リストが廃止されましたか?そうでない場合、一方が他方より優れている点は何ですか?どのような状況で宣言時の初期化が有利になりますか、または初期化リストが有利になりますか?一方を他方に対して使用する必要があるのはいつですか?

64
Vector

いいえ、この記事 Get the Know to the New C++ 11 Initialization Forms は、Class Member Initializationセクション(emphasis mine):

同じデータメンバーのコンストラクタにクラスメンバー初期化子とmem-initの両方がある場合、後者が優先されることに注意してください。実際、この動作を利用するには、メンバーのデフォルト値を指定することで利用できますコンストラクターには、そのメンバーの明示的なmem-initがありません。そうでない場合、コンストラクタのmem-initが有効になり、クラスメンバー初期化子がオーバーライドされます。 この手法は、複数のコンストラクターを持つクラスで役立ちます

そのため、クラスメンバーの初期化はニースの便利さですが、初期化リストの必要性はなくなりませんが、代わりに両方の機能が連携してデフォルト値を指定し、必要に応じてそれらをオーバーライドするニースの方法を提供します。これは、 Bjarne Stroustrup もそれをどのように見ているのかと思われます。

これにより入力の手間が省けますが、実際の利点は複数のコンストラクタを持つクラスにあります。多くの場合、すべてのコンストラクターはメンバーに共通の初期化子を使用します。

また、共通の初期化子を持つメンバーの例を示します。

class A {
  public:
    A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
    int a, b;
  private:
    HashingFunction hash_algorithm;  // Cryptographic hash to be applied to all A instances
    std::string s;                   // String indicating state in object lifecycle
};

そして言う:

Hash_algorithmとsにそれぞれ単一のデフォルトがあるという事実は、コードの混乱で失われ、メンテナンス中に簡単に問題になる可能性があります。代わりに、データメンバーの初期化を除外できます。

class A {
  public:
    A(): a(7), b(5) {}
    A(int a_val) : a(a_val), b(5) {}
    A(D d) : a(7), b(g(d)) {}
    int a, b;
  private:
    HashingFunction hash_algorithm{"MD5"};  // Cryptographic hash to be applied to all A instances
    std::string s{"Constructor run"};       // String indicating state in object lifecycle
};

注:C++ 11の欠点

C++ 11のクラスメンバーの初期化を使用すると、クラスが非集約になり、 aggregate initialization を使用できなくなるため、不利な点が1つあります。これは、この制限が削除されたC++ 14の場合ではありません。参照: C++ 11非初期化メンバーを持つクラスの初期化の初期化 詳細については.

68
Shafik Yaghmour

いいえ、廃止されていません。

クラスメンバを初期化するためにコンストラクタの引数が必要な場合、初期化リストはまだ唯一の方法です。

class A
{
 int a=7; //fine, give a default value
public:
 A();
};

class B
{
 int b; 
public:
 B(int arg) : b(arg) {}

 B(int arg, bool b) : b(arg) { ... }
};

両方が存在する場合、コンストラクタのinitが有効になり、クラスメンバーの初期化をオーバーライドします。これは、クラスメンバーのデフォルト値を指定するのに便利です。

7
quantdev

私の見方では、クラス内の初期化はmem-initializer-listの拡張です。 C++ 03では、mem-initializer-listにリストされていないメンバーは常にデフォルトで初期化されました。これは、クラスのデフォルトコンストラクターであり、プリミティブ型の初期化はありません。

クラス内の初期化では、独自のデフォルトを指定できます。それを見るには2つの方法があります。

1つ:クラスのほとんど/すべてのコンストラクターがメンバーに同じ初期値を提供する場合、そのメンバーにクラス内の初期化子を使用します。他のメンバーには、mem-initializer-listsを使用します。もちろん、初期値がコンストラクターの引数に依存する場合は常にこれらを使用する必要があります。

もう1つは、すべてのメンバーにクラス内の初期化子を提供します。クラスのデフォルトコンストラクターがそれらを初期化する方法とまったく同じです。次に、デフォルト以外のコンストラクターのmem-initializer-listsは、「デフォルトで構築されたオブジェクトとの違い」というセマンティクスを取得します。

5
Angew