如何验证 C++ 构造函数中的输入参数?

Posted

技术标签:

【中文标题】如何验证 C++ 构造函数中的输入参数?【英文标题】:How to validate input parameters in C++ constructor? 【发布时间】:2018-12-06 20:58:33 【问题描述】:

以下示例显示了问题的症结所在。我需要初始化一个类的 const 成员。这只能在初始化列表中完成,而不能在构造函数主体中完成。如果构造函数的输入无效,即向量大小小于 3,我想断言或抛出错误。

class A 
 // In following constructor, how do we make sure if params.size()
 // is at least 3.
 A(const std::vector<int>& params):
  x(params[0]), y(params[1]), z(params[2]) 
private:
  const int x;
  const int y;
  const int z;
;

请告知如何在 Modern C++(11 及更高版本)中实现这一目标

【问题讨论】:

使用 std::at() 代替 operator[]。 请注意const 数据成员带有限制。您将无法分配给A。通常,由于类型的接口不提供更改它们的方法,除了为对象分配一个全新的A 值之外,不变成员保持不变。 值得注意的是,如果您手头有一个兼容 c++20 的编译器,这对于 contracts 来说是一个非常好的用例。 我读对了吗?违反此约束是编程错误。为什么使用 std::vector 而不是 3 个参数? @JVApen,在这种特殊情况下同意,使用 std::vector 没有多大意义。因此,请不要过多阅读示例。示例只是为了说明有关如何在构造器参数用于初始化列表之前验证构造函数参数的实际问题。 【参考方案1】:

只需添加一个抽象层。您可以编写一个函数来确保向量的大小正确,甚至可以确保值在预期范围内(如果有的话)。看起来像

class A 
 A(const std::vector<int>& params):
  x(verify(params, 0)), y(verify(params, 1)), z(verify(params, 3)) 
private:
  static int verify(const std::vector<int>& params, int index) 
   
    if (params.size() < 4) // or use if (params.size() <= index) if you only care if the index will work
      throw something; 
    return params[index]; 
  
  const int x;
  const int y;
  const int z;
;

【讨论】:

size() &lt;= indexsize() != 4 更有意义 @RemyLebeau 如果大小小于 4,OP 会特别指出错误。我想我应该使用 params.size() &lt; 4 来满足这一点。 OP 说“如果向量大小低于 4”以及“如果 params.size() 至少为 3” ,所以这有点矛盾。就个人而言,我认为 4 是一个错字,索引 3 应该是 2。在 xyz 值之间存在间隙的情况下,传入一个 4 元素向量没有多大意义。三元素向量更有意义。但无论哪种方式,size() &lt;= index 在您的 verify() 函数中仍然更有意义,因为它正在执行边界检查以确保提供的 index 有效 @RemyLebeau 非常正确。至少现在它应该向他们展示他们想做的任何方式。 我认为这是一个解决方案。但是,这感觉就像一个黑客。看起来 C++ 没有更具体的方法来验证构造函数参数。我们可以执行以下操作以使其不那么冗长。 x(verify(params) ? params[0] : assert(0)), y(params[1]), z(params[2])【参考方案2】:

const 成员只能在构造函数的成员初始化列表中进行初始化。要验证调用者的输入,您必须调用辅助函数来验证每个输入值,然后再将其传递给相应的成员,例如:

int check(const std::vector<int> &params, int index) 
  if (params.size() <= index) throw std::length_error("");
  return params[index];


class A 
 A(const std::vector<int>& params):
  x(check(params, 0)), y(check(params, 1)), z(check(params, 3)) 
private:
  const int x;
  const int y;
  const int z;
;

或者,直接使用vector's own built-in bounds checking:

class A 
 A(const std::vector<int>& params):
  x(params.at(0)), y(params.at(1)), z(params.at(3)) 
private:
  const int x;
  const int y;
  const int z;
;

【讨论】:

【参考方案3】:

不像其他解决方案那样优雅,但是......您可以在第一个常量的初始化内的三元运算符中简单地添加一个throw

class A
 
   private:
      const int x;
      const int y;
      const int z;

   public:    
      A (const std::vector<int>& params)
         : x params.size() < 4u ? throw std::runtime_error"!"
                                 : params[0] ,
           yparams[1], zparams[3]
       
 ;

题外话建议:如果Aclass,那么构造函数最好是public

【讨论】:

【参考方案4】:

通过转换的其他选项额外层:

class A_params
   friend class A;
   int x;
   int y;
   int z;
   void validate();
   public:
   A_params(std::initializer_list<int>);
   A_params(const std::vector<int>&);
   A_params(int(&)[3]);
   //...
   ;


class A 
 // In following constructor, how do we make sure if params.size()
 // is at least 3.
public:
 A(A_params params):
  x(params.x), y(params.y), z(params.z) 
private:
  const int x;
  const int y;
  const int z;
;

【讨论】:

这只是将验证问题转移到A_params @FrançoisAndrieux 验证不是问题!问题是在成员列表初始化之前执行代码和平。 C'est difficile l'anglais non? 在实现A_params::A_params(const std::vector&lt;int&gt;&amp;) 时也会遇到同样的问题。这就是我将问题转移到A_params 的意思。 Oui, l'anglais est parfois difficile : 和平 = paix et piece = morceau. 我想我没抓住重点。使用成员初始化的目的是因为“A”的成员是const,如果这是唯一的原因,那么这个答案将起作用,因为A_params 不需要const 成员。

以上是关于如何验证 C++ 构造函数中的输入参数?的主要内容,如果未能解决你的问题,请参考以下文章

C++:调用无参数的构造函数为啥不加括号

c++中的构造函数

c++ 当我创建结构数组时,如何使用结构数组内的类的参数调用构造函数?

C++中复制构造函数被调用的三种情况

C++ 如何实现这一参数构造函数?

在继承 C++ 中调用 main 中的参数化构造函数