构造函数中的这种情况是正确的还是有更好的方法来做到这一点?
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;
};
C++ 中在构造时验证对象(即防止创建无效对象)的一般模式是在构造函数中抛出异常。这样,您可以保证如果您有一个 Foo 类型的对象,它将被正确初始化或根本不被创建。
所以你会做一些类似的事情:
class Foo{
public: Foo(int y) {
if (y < 0 || y > 99)
throw std::invalid_argument("y out of range");
x = y;
}
};
在实例化时,您可以捕获如下异常:
try
{
Foo f(100);
// do stuff with f
}
catch(std::invalid_argument& e)
{
std::cout << "Construction of Foo failed" << std::endl;
}
如果你没有捕捉到它,它就会冒泡,你可以在更高的层次上捕捉到它,或者根本不捕捉它,这将导致中止并退出应用程序。一般来说,异常应该被捕获在一个明显的地方,即如何正确地对异常情况做出反应(比如在最简单的情况下向用户显示错误并继续执行)。
不,如果你有构造函数的先决条件,你可以检查它们,但如果它们失败,你应该抛出异常或中止,而不仅仅是输出到std::cout
. 使用
assert( y >= 0 && y <= 99 )
将是这样做的典型方式。(您永远不应该将错误消息输出到std::cout
。)
在您的情况下,尽管出现错误消息,您的构造函数仍将“工作” - 它会创建对象,但状态无效。您可以使用异常来防止创建您的对象,因此您不会对现有的无效状态对象产生问题。
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
{
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 对象的存储限制在运行时堆中,并且出于示例目的而过度简化。有关如何实现工厂方法的更多信息。
这是另一种方法。这个确切的代码假定您不想从构造函数中抛出异常,而是希望有机会检查有效性,并且仅在实际使用无效值时才抛出异常。如果初始化无效值是“正常”情况,而不是任何“异常”情况,则使用这种方法是合理的,否则最好直接从构造函数中抛出。
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
。