2016-07-13 6 views
5

私はC++を手に入れようとしているJava開発者です。 setterが提供する健全性チェックを再利用するために、コンストラクタ内でsetterを使用するのは大丈夫ですか?例えばコンストラクタでセッターを使用する

#include <stdexcept> 
using namespace std; 

class Test { 
    private: 
     int foo; 
     void setFoo(int foo) { 
      if (foo < 42) { 
       throw invalid_argument{"Foo < 42."}; 
      } 

      this->foo = foo; 
     } 

    public: 
     Test(int foo) { 
      setFoo(foo); 
     }; 
}; 
+0

それは有効です。ここでは、テストを取り除くために 'unsigned'型が存在することに注意してください。 – Jarod42

+0

クラスのユーザーが負の数を渡さないようにしたい場合は、代わりに 'unsigned'を使用してみてください。次に、コンパイラは、コンパイル時に、ランタイムチェックが必要ではなく、チェックを処理します。 –

+2

'署名されていない 'ものは、明白ではありません。例えば、 https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 9:50、42:40、1:02:50(委員会のいくつかの著名なメンバーが主張するパネル –

答えて

5

はい、これは基本的に前述の理由からこれを行うことをお勧めします。

一方、セッターが必要なのか、コンストラクター内のチェックを直接実装していないのか自分自身に尋ねるべきです。私がこれを書いている理由は、一般にセッターは不変クラスとは対照的な多くの不利な点を持つ可変状態になるということです。しかし時々彼らは必要とされます。

別勧告:

class MyFoo { 
public: 
    MyFoo(int value) { 
     if (value < 42) { 
      throw invalid_argument{"Foo < 42."}; 
     } 
     v = value; 
    } 
private: 
    int v; 
} 

これは使用することができるようになります:あなたのクラス変数がオブジェクトであり、あなたは、このオブジェクトのコンストラクタを変更することができた場合は、このオブジェクトのコンストラクタにチェックを入れることができますあなたのTestクラスのコンストラクタで初期化リストは:

Test(int foo) : foo(foo) {} 

しかし、今のチェックは、変数のクラスのプロパティと所有クラスの、もはや一つです。

1

はい、それは限り、それは特定のメンバ変数のsetter(唯一例えば割り当てによって確認することができないいくつかのロジックを持っている)を持っていることは理にかなっていて大丈夫です。この例では、setFooはちょうどunsigned intになり、発信者は負の値を渡さないことがわかります。これは、チェックを省略することができ、したがってセッターの必要性を排除することができます。より精巧なチェックのために、コンストラクタ内のそのセッターのセッターと使用法は問題ありません。

3

はいできます。あなたのセッターがvirtualでない限り、これはうまくいきます。なぜなら、 "this" ptrはまだ準備ができていないので、それは右の関数を呼び出す際の継承階層なのです。ここで

は、この問題にハーブサッターGOTWです: http://www.gotw.ca/gotw/066.htm

+0

@ user2079303このような状況でどの関数が呼び出されるかをdevが認識している限り、それは問題ないと私は同意します。私は、バーチャルオンスを呼び出す非バーチャルファンクションを呼び出すことから来るエラーについては決して考えなかった。いい視点ね。 – paweldac

+0

@ user2079303:_ "絶対に気にしないことは、メンバーの関数を呼び出すことですあなたが言ったことと違うのはどのように安全ですか? –

+0

@ user2079303: 'そうだ。非コンストラクタは動的ディスパッチを行い、コンストラクタは動的ディスパッチを行います。結果は確定的です。あなたがより派生したオブジェクトを構築しているのであれば、それはあなたが期待していたものではないかもしれませんが、[安全だと確信しています](http://coliru.stacked-crooked.com/a/9a0f6498a5df9acb)。工事が完了した後 'バー()'の仮想ディスパッチは、最も派生クラス 『について知っている』ということを示す –

1

短い答え:はい。実際には、あなたの例が動作します。

長い答え:しかし、それは良い習慣ではありません。少なくとも、世話をする必要があります。

一般に、set関数は構築されたオブジェクトで動作します。クラスの不変量が成り立つと仮定します。 invariantがtrueであることを考慮して、クラス内の関数が実装されます。

他の関数をコンストラクタで使用する場合は、コードを記述する必要があります。たとえば、空のオブジェクトを作成します。

たとえば、あなたのクラスで将来的にsetFooを変更した場合(setFooがメンバーfooを大きく変更するとします)、作業を中止します。

0

私はあなたの状況に合っていないことを知っています。

メンバーの値を(setFooのようなチェックなしで)設定するだけでは、コンストラクタで初期化リストを使用することをお勧めします。これによりメンバーは2回初期化されます:1.デフォルト値で2.コンストラクタに渡した値で

class Test { 
private: 
    int foo_; 

public: 
    Test(int foo) 
     : foo_(foo) 
    { }; 
}; 
関連する問題