コンストラクターのこの条件は正しいですか、それともこれを行うためのより良い方法はありますか?
class Foo {
public: Foo(int y) {
if (y < 0 || y > 99)
cout << "Error! Invalid input" << endl;
else
x = y;
}
private: int x;
};
コンストラクターのこの条件は正しいですか、それともこれを行うためのより良い方法はありますか?
class Foo {
public: Foo(int y) {
if (y < 0 || y > 99)
cout << "Error! Invalid input" << endl;
else
x = y;
}
private: int x;
};
あなたの場合、エラーメッセージにもかかわらず、コンストラクターは引き続き「機能」します-オブジェクトを作成しますが、状態は無効です。オブジェクトの作成を防ぐために例外を使用することができるので、無効な状態の既存のオブジェクトに問題はありません。
class InvalidInputException: public std::exception {
// ...
};
class Foo{
public: Foo(int y) {
if (y < 0 || y > 99)
throw InvalidInputException();
x = y;
}
};
// Test:
try
{
Foo f(param);
}
catch(InvalidInputException& e)
{
std::cerr << "Invalid input!" << std::endl;
}
また、独自の例外クラスを作成する必要はありません。標準ライブラリには、 std::runtime_errorやstd::logic_error (およびそのサブクラス) など、適切な例外タイプがある場合があります。
ここにさらに別のアプローチがあります。この正確なコードは、コンストラクターから例外をスローしたくないことを前提としていますが、代わりに有効性をチェックする機会を与え、無効な値が実際に使用されている場合にのみ例外をスローしたいと考えています。無効な値の初期化が「例外的」ではなく「通常」の状況である場合、このアプローチを使用することは正当化できます。それ以外の場合は、コンストラクターから直接スローする方がよいでしょう。
class Foo {
public:
Foo(int y) :
valid(y >= 0 && y <= 99),
x(valid ? y : -1) // use -1 as invalid value marker
{
// note: doing cout output from constructor is... unusual
if (!valid)
cout << "Error! Invalid input" << endl;
}
bool isValid() {
return valid;
}
int getX() {
if (!valid) throw something;
return x;
}
private:
bool valid;
int x;
}
そのコードがコンストラクタ初期化子リストも使用する方法に注意してください。この場合、メンバー変数の順序valid
はx
重要であり、初期化時にその値が使用されるため、valid
前にする必要があります。x
x
このような場合は、ファクトリ デザイン パターンを使用することをお勧めします。次のようになります。
class Foo
{
private:
int x;
Foo(int y)
{
x = y;
}
public:
static Foo* GenerateFoo(int y)
{
Foo* newFoo = NULL;
if(y >= 0 && y <= 99)
newFoo = new Foo(y);
return newFoo;
}
}
これにより、入力が範囲外の場合は作成されず、代わりに NULL ポインターが返されます。この特定の実装では、Foo オブジェクトのストレージがランタイム ヒープに制限されており、例として非常に単純化されていることに注意してください。Factory Method の実装方法に関する詳細情報が利用可能です。