1

多くのクラス メンバーとさまざまなコンストラクターを持つこのクラスがあります。

これまでは、所有している各コンストラクターでコンストラクター初期化リストを使用し、各メンバーを希望どおりに調整していました。

クラスに新しいメンバーを追加するたびに、各コンストラクターにアクセスし、初期化リストを更新してこのメ​​ンバーにデフォルト値を追加する必要があるため、これは非常に面倒です。

そこで、必要な値を初期化するメソッドを追加しようと考えました。問題!メソッドは初期化リストの後に実行されるため、この初期化リストに入力した特定の値はメソッドによってオーバーライドされます。

簡単な例:

class A
{
public:
  A();
  A( B b );
  A( int i );
  // A( .... ); plenty of them


private:
  int member1, m2, m3,m4;
  bool b1,b2, b3;
  // ....
  // every time I add a member I have to modify the initialization lists

  // solution: agregate member initialization in a member function: 
  void init_members();    
}

// init list constructors
A::A() : m1(false), m2(false), m3(false), m4(true) .... // looong list
{
}

A::A( B b) : m1(b.state()), m2(false), m3(false), ... // loong list
{
}

// problem, if I use init_members:
void A::init_members()
{
  m1 = false;
  m2 = false;
  m3 = false;
// ...
}

A::A( int i ) : m1( true)
{
  init_members(); // overrides m1 !!!
}

それで、私の質問:リスト初期化子とメソッド初期化子を混在させて、リスト初期化子がメソッド初期化子よりも優先されるようにすることはできますか? 上記の例では、m1 を最後のコンストラクター
にとどめたいと考えています。true

注:メソッド呼び出しのに初期化リストを移動できることはわかっていますが、これは値を members に 2 回割り当てることを意味しinit_members()ます。最適ではありません:-)

在庫があれば、ちょっとしたトリックを期待していました。

4

3 に答える 3

5

C ++ 11では、1つのオプションはコンストラクターの委任であり、コードの重複を避けるために、1つのコンストラクターが他のコンストラクターの1つを呼び出すだけです。

これはどのように見えるかです:

class A {
public:
    A(int,int,double);
    A(int,int);
    A(double);
    A();
private:
    ...
};

A::A(int a,int b,double c) {
    // the real work to initialize the class
}

// A(int,int) delegates to A(int,int,double), passing along a
// default value for the double
A::A(int a,int b) : A(a,b,0.0) {}

A::A(double c) : A(1,2,c) {} // A(double) delegates to A(int,int,double)

A::A() : A(1.0) {} // A() delegates to A(double)

ループを作成しないように注意してください。また、通常は、単一のコンストラクターで実際の作業のほとんどを実行し、他のコンストラクターではそのコンストラクターに渡したい値をマーシャリングする必要があります。これを「指定コンストラクター」と呼びます。指定されたコンストラクターは、ほとんどのパラメーターを取り、デフォルト値を使用しないコンストラクターである必要があります。最終的に、すべてのコンストラクターは、指定されたコンストラクターを直接または間接的に呼び出す必要があります。

パターンに注意してください。いくつかのデフォルト値を使用するコンストラクターは、デフォルトがまったくない関数に到達するまで、それらのデフォルトを、より少ないデフォルトを使用するコンストラクターに渡します。これは、メソッドで実行しようとしていることの反対ですinit_members()。すべてのデフォルトを設定する関数があり、それらのいくつかをオーバーライドしようとします。C ++ 11の機能を使用できない場合は、指定されたコンストラクターパターンをエミュレートすることをお勧めしinit_members()ます。指定された初期化子になり、デフォルトはありません。コンストラクターごとにイニシャライザーメソッドを使用できます。このメソッドは、指定された引数を受け取り、いくつかのデフォルト値をスローして、別のinit_membersオーバーロードを呼び出します。

ただし、指定された初期化子/コンストラクターの問題の1つは、デフォルトがいたるところに散在していることです。委任以外のC++11の別のオプションは、「クラス内初期化」です。これにより、すべてのデフォルト値をまとめることができます。

class A {
public:
    A(int,int,double);
    A(int,int);
    A(double);
    A();
private:
    int a = 1,b = 2; // in-class initialization gathers all the defaults together
    double c = 1.0;
};

上記の場合、すべてのコンストラクターは、そのコンストラクターで他の何かに明示的に初期化しない限り、メンバー値をそれらのデフォルトに自動的に初期化します。

A::A(int a,int b,double c) : a(a), b(b), c(c) {}
A::A(int a,int b) : a(a), b(b) {} // member c is automatically initialized to 1.0
A::A(double c) : c(c) {} // members a and be are automatically initialized to 1 and 2
A::A() {}; // all members are initialized with their in-class values.

使用例を次に示しinit_members()ます。

class A {
public:
    A(int a,int b,double c) { init_members(a,b,c); }
    A(int a,int b) { init_members(a,b); }
    A(double c) {init_members(c);}
    A() { init_members(); }
private:
    void init_members(int,int,double) { ... }
    void init_members(int a,int b) { init_members(a,b,1.0); }
    void init_members(double c) { init_members(1,2,c); }
    void init_members() { init_members(1.0); }
    ...
};

このメソッド値init_members()は、呼び出す前にメンバーを初期化するため、メンバーは2回初期化されます。C++03でそれを修正する方法があるかどうかはわかりません。

于 2012-03-28T17:23:27.457 に答える
1

このような場合、私はBase / Member Initializer Listを使用せず(メンバーはコンストラクターのその時点で「garbage」または「default-constructor」の値を持ちます)、init_()関数(コンストラクター本体から呼び出されます)を使用します。次に、コンストラクターがinit_()関数を呼び出し、単一のメンテナンスポイントがあります。

同様に、私の関数も、「デフォルト」値の単一のメンテナンスポイントに対して関数をclear()呼び出します。init_()

あなたの場合、それは次のようになります:

A::A(void)
//...no Base/Member-Initializer list...
{
  init_members();
}

A::clear(void)
{
  init_members();
}

...そしてオーバーライド:

A::A(int override_m1)
{
  init_members();
  m1 = override_m1;
}
于 2012-03-28T15:58:21.600 に答える
0

うーん、あなたの同僚がこのようなクラスを見つけた場合、あなたにどんな名前を付けるか知りたくありません.彼らがこの種のモンスターを修理または拡張しなければならないとしたらどうなるか想像してみてください. コードベースにある多くの の一部を次のように切り替えるタスクを新たに割り当てましたA(これは保守できなくなりました)。

class AKeyValueStorage {
    // would be some kind of shared storage if meant to be 
    // copyable and don't forget moving if your're on c++11!
    std::map<std::string, boost::any> mMembers;
public:
    template<class Key, class T>
    T const & Get(Key const & pKey) const
    {
        auto tTmp = mMembers.find(ToString(pKey));
        if (tTmp != mMembers.end()) {
            return boost::any_cast<T const &>(*tTmp); 
        }
        // throw if none, or return default
    }

    template<class Key, class T>
    void Set(Key const & pKey, T const & pValue) const
    {
        mMembers[ToString(pKey)] = pValue; // replace if found, insert if none
    }
};
于 2012-03-28T16:34:07.217 に答える