2010-05-20 58 views
1

我有一个看起来像这样的设置。抛出异常是一种健康的退出方式吗?

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

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

bool Checker::Check() 
{ // initilisations etc. 
    Check1(); 
    Check2(); 
    // ... so on 
} 

一个典型的检查功能应该是这样的:

bool Checker::Check1() 
{ double result; 
    // lots of code 
    m_results.SetCheck1Result(result); 
} 

而且结果类看起来是这样的:

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

注:所有代码过于简单。

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

  • 修改每个CheckN函数以保持检查结果和返回。父Check函数将继续检查m_results。 OR
  • 在Results :: SetCheckNResults()中,如果值超过1,则引发异常,并在Checker :: Check()结束时捕获它。

首先是繁琐,容易出错,次优的,因为每一个功能CheckN进一步分支出来成子支票等

第二是非侵入和快捷。一个缺点是我能想到的是,Checker代码可能不一定是异常安全的(尽管其他地方没有引发其他异常)。还有什么是我忽略的显而易见的东西?抛出异常和堆栈展开的成本是多少?

有更好的第三种选择吗?

+0

是否有一个速度/资源需求?看起来如此,但你并没有完全清楚。 – Carlos 2010-05-20 08:28:52

+0

卡洛斯,是的,有一个速度要求。我试图实现的新模式只需要知道结果是否超过1,并且必须尽快返回ASAP。 – ramaseshan 2010-05-20 08:47:13

回答

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

编辑:我忽略了你需要调用任何N个SetCheckResultX()功能,太,这将是不可能纳入我的示例代码。因此,无论你硬塞到一个数组这也(将其更改为SetCheckResult(std::size_t idx, double result)),或者你就必须有每个表条目TWO函数指针:

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

(而且,不,我不会来试图写下一个成员函数指针的类型正确的语法。我一直有看这件事,并仍然没有加时赛这一权利的第一次......但我知道这是可行的。)

+0

+1几乎相同的单词甚至,但你键入更快:-) – 2010-05-20 08:31:53

+0

@佩特:我可能刚刚开始打字。 ':)' – sbi 2010-05-20 08:32:26

+0

啊,所以_this_是秘密! :-) – 2010-05-20 08:39:26

2

例外都是为了在正常操作过程中不应发生的情况。他们几乎没有侵入性;它们的本质包括解除调用堆栈,调用各处的析构函数,将控制权移交给另一部分代码等。这些东西可能很昂贵,这取决于您最终做了多少事情。

即使它是免费的,但是,使用异常作为一个正常的流量控制机制是另外一个,很大的原因是一个坏主意:不意味着异常被使用的方式,这样的人不以这种方式使用它们,这样他们就会看着你的代码并且摸不着头脑,试图弄清楚为什么你要抛出看起来像是错误的东西。头部划伤通常意味着你正在做比你应该更聪明的事情。

+2

如果你正常返回,所有的破坏等都必须发生,如果这样很昂贵,那么例外情况不会增加大量的开销。然而,我同意,例外情况不应用于正常的控制流程。 – sbi 2010-05-20 08:53:43