抛出异常是一种健康的退出方式吗?

1 c++ exception

我有一个看起来像这样的设置.

class Checker
{   // member data
    Results m_results; // see below
 public:
    bool Check();
 private:
    bool Check1();
    bool Check2();
    // .. so on
};
Run Code Online (Sandbox Code Playgroud)

Checker是一个为工程分析执行冗长检查计算的类.每种类型的支票都有一个检验员存储的结果.(见下文)

bool Checker::Check()
{   // initilisations etc.
    Check1();
    Check2();
    // ... so on
}
Run Code Online (Sandbox Code Playgroud)

典型的Check函数如下所示:

bool Checker::Check1()
{   double result;
    // lots of code
    m_results.SetCheck1Result(result);
}
Run Code Online (Sandbox Code Playgroud)

结果类看起来像这样:

class Results
{   double m_check1Result;
    double m_check2Result;
    // ...
public:
    void SetCheck1Result(double d);
    double GetOverallResult()
    { return max(m_check1Result, m_check2Result, ...); }
};
Run Code Online (Sandbox Code Playgroud)

注意:所有代码都过于简单了.

最初编写Checker和Result类以执行所有检查并返回整体双重结果.现在有一个新的要求,我只需要知道任何结果是否超过1.如果是,则不需要执行后续检查(这是一个优化).为实现这一目标,我可以:

  • 修改每个CheckN函数以检查结果并返回.父检查功能将继续检查m_results.要么
  • 在Results :: SetCheckNResults()中,如果值超过1则抛出异常并在Checker :: Check()结束时捕获它.

第一个是繁琐,容易出错和次优,因为每个CheckN函数进一步分支到子检查等.

第二个是非侵入性和快速的.我可以想到的一个缺点是Checker代码可能不一定是异常安全的(尽管在其他地方没有抛出其他异常).还有什么是我能忽视的明显事物吗?抛出异常和堆栈展开的成本怎么样?

有更好的第三选择吗?

sbi*_*sbi 10

我不认为这是个好主意.例外情况应限于特殊情况.你的是正常控制流程的问题.

看起来你可以很好地将所有处理结果的冗余代码移出检查并进入调用函数.生成的代码比非异常异常更清晰,更容易理解.
更改CheckX()函数以返回double它们生成的函数,并将结果处理给调用者.调用者可以更容易地以不涉及冗余的方式执行此操作.
如果你想要真正的花哨,把这些函数放入一个函数指针数组并迭代它.然后处理结果的代码将全部循环.就像是:

bool Checker::Check()
{
  for( std::size_t id=0; idx<sizeof(check_tbl)/sizeof(check_tbl[0]); ++idx ) {
    double result = check_tbl[idx]();
    if( result > 1 ) 
      return false; // or whichever way your logic is (an enum might be better)
  }
  return true;
}
Run Code Online (Sandbox Code Playgroud)

编辑:我忽略了你需要调用任何N个SetCheckResultX()函数,这些函数不可能包含在我的示例代码中.所以要么你也可以把它变成一个数组,(把它们改成SetCheckResult(std::size_t idx, double result))或者你必须在每个表项中有两个函数指针:

struct check_tbl_entry {
  check_fnc_t checker;
  set_result_fnc_t setter;
};

check_tbl_entry check_tbl[] = { { &Checker::Check1, &Checker::SetCheck1Result }
                              , { &Checker::Check2, &Checker::SetCheck2Result }
                              // ...
                              };

bool Checker::Check()
{
  for( std::size_t id=0; idx<sizeof(check_tbl)/sizeof(check_tbl[0]); ++idx ) {
    double result = check_tbl[idx].checker();
    check_tbl[idx].setter(result);
    if( result > 1 ) 
      return false; // or whichever way your logic is (an enum might be better)
  }
  return true;
}
Run Code Online (Sandbox Code Playgroud)

(而且,不,我不会尝试写下成员函数指针类型的正确语法.我总是不得不查看它并且仍然永远不会在第一次这样做...但我知道它是可行的.)