2010-05-20 52 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