web-dev-qa-db-ja.com

constメンバー変数を初期化します

私は次のようなものに要約されるC++コードを持っています:

class Foo{
    bool bar;
    bool baz;
    Foo(const void*);
};
Foo::Foo(const void* ptr){
    const struct my_struct* s = complex_method(ptr);
    bar = calculate_bar(s);
    baz = calculate_baz(s);
}

意味的には、barおよびbazメンバー変数は、初期化後に変更されるべきではないため、constである必要があります。ただし、そうするためには、割り当てるのではなく、初期化リストで初期化する必要があるようです。明確にするために、私は理解していますなぜ私はこれをする必要があります。問題は、次の望ましくないことのいずれかを実行せずに、コードを初期化リストに変換する方法を見つけることができないように思われることです。

  • complex_methodを2回呼び出します(パフォーマンスに悪影響があります)
  • Fooクラスへのポインターを追加します(クラスサイズが不必要に大きくなります)

これらの望ましくない状況を回避しながら変数をconstにする方法はありますか?

24
dooglius

C++ 11コンパイラを購入できる場合は、 コンストラクタの委任 を検討してください。

class Foo
{
    // ...
    bool const bar;
    bool const baz;
    Foo(void const*);
    // ...
    Foo(my_struct const* s); // Possibly private
};

Foo::Foo(void const* ptr)
    : Foo{complex_method(ptr)}
{
}

// ...

Foo::Foo(my_struct const* s)
    : bar{calculate_bar(s)}
    , baz{calculate_baz(s)}
{
}

一般的なアドバイスとして、データメンバーをconstとして宣言する場合は注意が必要です。これにより、クラスでコピー代入とムーブ代入が不可能になるためです。クラスが値セマンティクスで使用されることになっている場合、それらの操作が望ましいものになります。そうでない場合は、このメモを無視してかまいません。

27
Andy Prowl

他の回答で説明されているように、1つのオプションはC++ 11委任コンストラクターです。 C++ 03互換のメソッドは、サブオブジェクトを使用することです。

class Foo{
    struct subobject {
        const bool bar;
        const bool baz;
        subobject(const struct my_struct* s)
            : bar(calculate_bar(s))
            , baz(calculate_baz(s))
        {}
    } subobject;
    Foo(const void*);
};
Foo::Foo(const void* ptr)
    : subobject(complex_method(ptr))
{}

barbaz constを作成するか、subobject constを作成するか、またはその両方を行うことができます。

subobject constのみを作成する場合は、complex_methodを計算し、barのコンストラクター内でbazsubobjectに割り当てることができます。

class Foo{
    const struct subobject {
        bool bar;
        bool baz;
        subobject(const void*);
    } subobject;
    Foo(const void*);
};
Foo::Foo(const void* ptr)
    : subobject(ptr)
{}
Foo::subobject::subobject(const void* ptr){
    const struct my_struct* s = complex_method(ptr);
    bar = calculate_bar(s);
    baz = calculate_baz(s);
}

reasonコンストラクター本体内のconstメンバーを変更できないのは、一貫性を保つために、コンストラクター本体が他のメンバー関数本体と同じように扱われるためです。コードをコンストラクターからメンバー関数に移動してリファクタリングすることができ、ファクタリングされたメンバー関数は特別な処理を必要としないことに注意してください。

12
ecatmur

C++ 11ではデリゲートコンストラクターを使用できます。

class Foo{
public:
    Foo(const void* ptr) : Foo(complex_method(ptr)) {}

private:
     Foo(const my_struct* s) : bar(calculate_bar(s)), baz(calculate_baz(s)) {}

private:
    const bool bar;
    const bool baz;
};
8
Jarod42

Newfangled委任コンストラクターを使用したくない場合(私はまだそれらについて知らないコンパイラーバージョンを処理する必要があります)、クラスのレイアウトを変更したくない場合は、解決策を選ぶことができますこれは、コンストラクターを_const void *_引数に置き換え、Fooを返す静的メンバー関数を使用しますが、_complex_method_からの出力を引数として受け取るプライベートコンストラクターを持ちます(後者は、コンストラクターの委任例によく似ています)。 )。次に、静的メンバー関数は、_complex_method_を含む必要な予備計算を実行し、return Foo(s);で終了します。これには、クラスの呼び出し(returnステートメント内)がおそらく省略できる場合でも、クラスにアクセス可能なコピーコンストラクターが必要です。

2