如何检查类中传递的参数是否正确

How to check if passed arguments in a class are correct

本文关键字:参数 是否 何检查 检查      更新时间:2023-10-16

假设以下代码:

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

是的,你可以这样做。你只需要验证构造函数体内的参数。如果它们无效,则抛出异常。

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

你可以做这样的事情:

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

等等。请注意重要的一点,如果在构造函数中抛出异常,则不会调用析构函数(尽管调用任何基类析构函数)。这是内存泄漏的一个常见原因。

在我开始之前,我想澄清一下,这实际上是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不要使用原始指针,使用智能指针)。

您可以使用

static_assert来实现编译时检查,但也许您必须将调用包装在一个丑陋的宏中,或者可能包含在模板中。

像这样(希望不那么丑陋):

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

由于您在程序编写时知道可接受的值范围,因此尝试使用不正确的值构造类意味着您的程序已中断。 您应该使用断言。 断言用于记录类/函数/等的正确用法,并简化调试过程。
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 的另一个优点是,当您编译发布/优化构建时,断言的计算结果将为 null 语句。 这样,旨在帮助您调试的代码不会减慢您的发布版本。

记得#include <assert.h>.

这就是我的做法

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