c++ 如何检查传递给构造函数的参数是否正确,如果不正确,则停止创建对象

0wi1tuuw  于 12个月前  发布在  其他
关注(0)|答案(6)|浏览(199)

下面是一个例子:

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的创建?

ntjbwcob

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 ();
       ...             
    }
};

字符串

bvjveswy

bvjveswy2#

你可以这样做:

myClass(int a, int b, int c)
{
    if (a <= 1){
        throw something; // ToDo - define `something`, a text string would work.
    }
}

字符串
等等。注意一个重要的点,如果在构造函数中抛出异常,析构函数将被调用(尽管任何基类析构函数被调用)。这是内存泄漏的一个相当常见的原因。

w8ntj3qf

w8ntj3qf3#

在我开始之前,我想澄清这实际上是C++中一个相当大的主题,许多设计模式都是围绕这个问题显式设计的。
一个更好的方法是在构造器中抛出一个异常:

class myClass {
public:
  myClass(int a, int b, int c) 
  {
    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 c;
 c.initialize(2,5,6);


我个人不喜欢这样,因为你最终与僵尸类。

myClass c;
 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_;
 };


(protip不要使用原始指针,使用智能指针)。

8cdiaqws

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;
}

字符串

6qftjkof

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); 
    };
};

字符串
assert将抛出一个异常,如果你传递给它的布尔值为false。
通过说assert(a > 1 && b > 2 && c > 2);,你是说“程序永远不应该用超出正确范围的a、b和c的值来构造myClass”。如果程序这样做了,程序是不正确的。这将使你很容易找到并纠正错误。
如果值来自于你无法控制的地方,比如用户输入,你应该在myClass的构造函数之外验证输入。这是正确的关注点分离。
使用assert的另一个优点是,当你编译你的发布/优化版本时,Assert将计算为null语句。这样,旨在帮助你调试的代码将不会减慢你的发布版本。
记住#include <assert.h>

vbkedwbf

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;
    }
 }

字符串

相关问题