下面是一个例子:
class myClass{ myClass(int a, int b, int c){};};main(){ myClass cl(2,5,6);}
class myClass{
myClass(int a, int b, int c){};
};
main(){
myClass cl(2,5,6);
}
字符串myClass cl(2,5,6);将工作。但是如果我希望构造函数只与特定值一起工作呢?例如a>1 b>2 c>1。有没有什么方法可以检测错误的参数并在构造函数中“取消”cl的创建?
ntjbwcob1#
是的,你可以这样做。你只需要验证构造函数体中的参数。如果它们无效,那么抛出异常。
Class Invalid{ private: int m_x, m_y; public : class MyException : public exception {}; Invalid ( int x, int y ) { if ( x < 0 || y > 100 ) throw MyException (); ... }};
Class Invalid
{
private:
int m_x, m_y;
public :
class MyException : public exception {};
Invalid ( int x, int y )
if ( x < 0 || y > 100 )
throw MyException ();
...
字符串
bvjveswy2#
你可以这样做:
myClass(int a, int b, int c){ if (a <= 1){ throw something; // ToDo - define `something`, a text string would work. }}
myClass(int a, int b, int c)
if (a <= 1){
throw something; // ToDo - define `something`, a text string would work.
字符串等等。注意一个重要的点,如果在构造函数中抛出异常,析构函数将不被调用(尽管任何基类析构函数将被调用)。这是内存泄漏的一个相当常见的原因。
w8ntj3qf3#
在我开始之前,我想澄清这实际上是C++中一个相当大的主题,许多设计模式都是围绕这个问题显式设计的。一个更好的方法是在构造器中抛出一个异常:
class myClass {public: myClass(int a, int b, int c) { if (a<=1 || b <= 2 || c<=1) throw "some exception"; }};
class myClass {
public:
if (a<=1 || b <= 2 || c<=1) throw "some exception";
字符串这通常被认为是一个不好的习惯,因为类的析构函数永远不会被调用!根据经验,构造函数应该快速简单。如果构造函数可能失败,你应该尝试其他方法。此外,异常处理在C++中是出了名的慢。所以很多人会使用初始化调用:
class myClass { public: myClass() { initialized_ = true;} void initialize((int a, int b, int c) { initialized_ = !(a<=1 || b <= 2 || c<=1);} bool alive() {return intialized_;} private: bool initialized_; };
myClass() { initialized_ = true;}
void initialize((int a, int b, int c) { initialized_ = !(a<=1 || b <= 2 || c<=1);}
bool alive() {return intialized_;}
bool initialized_;
型然后,当你使用这个类时,你可以在初始化尝试后检查对象是否成功。
myClass c; c.initialize(2,5,6);
myClass c;
c.initialize(2,5,6);
型我个人不喜欢这样,因为你最终与僵尸类。
myClass c; c.initialize(0,0,0); c.foo();//Legal, compiles, but is WRONG
c.initialize(0,0,0);
c.foo();//Legal, compiles, but is WRONG
型这个僵尸类反对RAII的想法,老实说,我不应该做检查所有的时间。我喜欢的处理方法是工厂方法。
class myClass { public: static myClass* makeMyClass(int a, int b, int c) { myClass* ret = new myClass(); ret->initialize(a,b,c); if (!ret->alive()) {delete ret; return null;} return ret; } private: myClass() { initialized_ = true;} void initialize((int a, int b, int c) { initialized_ = !(a<=1 || b <= 2 || c<=1);} bool alive() {return intialized_;} private: bool initialized_; };
class myClass
static myClass* makeMyClass(int a, int b, int c)
myClass* ret = new myClass();
ret->initialize(a,b,c);
if (!ret->alive()) {delete ret; return null;}
return ret;
型(protip不要使用原始指针,使用智能指针)。
8cdiaqws4#
您可以使用static_assert来实现编译时检查,但可能必须将调用 Package 在一个丑陋的宏中,或者 Package 在模板中。类似于(希望不那么丑陋):
class myClass{public: myClass(int a, int b, int c){};};#define SafeMyClass(obj, a,b,c) static_assert(a<b,"a<b"); static_assert(b<c,"b<c"); myClass obj(a,b,c);int main(){ SafeMyClass(cl,2,5,6); return 0;}
#define SafeMyClass(obj, a,b,c) static_assert(a<b,"a<b"); static_assert(b<c,"b<c"); myClass obj(a,b,c);
int main(){
SafeMyClass(cl,2,5,6);
return 0;
6qftjkof5#
因为你在写程序的时候就知道了可接受值的范围,所以试图用不正确的值构造类意味着你的程序已经失败了。你应该使用Assert。Assert用于记录类/函数/等的正确用法,并简化调试过程。http://www.cplusplus.com/reference/cassert/assert/
class myClass{ myClass(int a, int b, int c) { assert(a > 1 && b > 2 && c > 2); };};
myClass(int a, int b, int c) {
assert(a > 1 && b > 2 && c > 2);
字符串assert将抛出一个异常,如果你传递给它的布尔值为false。通过说assert(a > 1 && b > 2 && c > 2);,你是说“程序永远不应该用超出正确范围的a、b和c的值来构造myClass”。如果程序这样做了,程序是不正确的。这将使你很容易找到并纠正错误。如果值来自于你无法控制的地方,比如用户输入,你应该在myClass的构造函数之外验证输入。这是正确的关注点分离。使用assert的另一个优点是,当你编译你的发布/优化版本时,Assert将计算为null语句。这样,旨在帮助你调试的代码将不会减慢你的发布版本。记住#include <assert.h>。
assert
#include <assert.h>
vbkedwbf6#
我就是这么做的
class myClass{ public: myClass(int a, int b, int c):aValue(a), bValue(b), cValue(c){}; private: int aValue; int bValue; int cValue; }; myClass::myClass(int a, int b, int c){ if(a<2) throw rangeError("the 'a' should be larger than one"); if(b<3) throw rangeError("the 'b' should be larger than one"); if(c<2) throw rangeError("the 'c' should be larger than one"); } void main(){ try{ myClass cl(2,5,6); }catch(rangeError re){ cout << re.what() << endl; } }
myClass(int a, int b, int c):aValue(a), bValue(b), cValue(c){};
int aValue;
int bValue;
int cValue;
myClass::myClass(int a, int b, int c){
if(a<2) throw rangeError("the 'a' should be larger than one");
if(b<3) throw rangeError("the 'b' should be larger than one");
if(c<2) throw rangeError("the 'c' should be larger than one");
void main(){
try{
}catch(rangeError re){
cout << re.what() << endl;
6条答案
按热度按时间ntjbwcob1#
是的,你可以这样做。你只需要验证构造函数体中的参数。如果它们无效,那么抛出异常。
字符串
bvjveswy2#
你可以这样做:
字符串
等等。注意一个重要的点,如果在构造函数中抛出异常,析构函数将不被调用(尽管任何基类析构函数将被调用)。这是内存泄漏的一个相当常见的原因。
w8ntj3qf3#
在我开始之前,我想澄清这实际上是C++中一个相当大的主题,许多设计模式都是围绕这个问题显式设计的。
一个更好的方法是在构造器中抛出一个异常:
字符串
这通常被认为是一个不好的习惯,因为类的析构函数永远不会被调用!根据经验,构造函数应该快速简单。如果构造函数可能失败,你应该尝试其他方法。此外,异常处理在C++中是出了名的慢。
所以很多人会使用初始化调用:
型
然后,当你使用这个类时,你可以在初始化尝试后检查对象是否成功。
型
我个人不喜欢这样,因为你最终与僵尸类。
型
这个僵尸类反对RAII的想法,老实说,我不应该做检查所有的时间。
我喜欢的处理方法是工厂方法。
型
(protip不要使用原始指针,使用智能指针)。
8cdiaqws4#
您可以使用static_assert来实现编译时检查,但可能必须将调用 Package 在一个丑陋的宏中,或者 Package 在模板中。
类似于(希望不那么丑陋):
字符串
6qftjkof5#
因为你在写程序的时候就知道了可接受值的范围,所以试图用不正确的值构造类意味着你的程序已经失败了。你应该使用Assert。Assert用于记录类/函数/等的正确用法,并简化调试过程。
http://www.cplusplus.com/reference/cassert/assert/
字符串
assert
将抛出一个异常,如果你传递给它的布尔值为false。通过说
assert(a > 1 && b > 2 && c > 2);
,你是说“程序永远不应该用超出正确范围的a、b和c的值来构造myClass”。如果程序这样做了,程序是不正确的。这将使你很容易找到并纠正错误。如果值来自于你无法控制的地方,比如用户输入,你应该在myClass的构造函数之外验证输入。这是正确的关注点分离。
使用
assert
的另一个优点是,当你编译你的发布/优化版本时,Assert将计算为null语句。这样,旨在帮助你调试的代码将不会减慢你的发布版本。记住
#include <assert.h>
。vbkedwbf6#
我就是这么做的
字符串