2013-09-25 154 views
7

想象一下,我有一份工作要做,可以通過三種不同的方式來完成:一種緩慢而痛苦的方式,但是故障安全的方式;中等程度的痛苦的方式,給你有Resource1;和一個快速簡單的方法,這需要Resource1Resource2。現在,這些資源是珍貴的,所以我將它們包裝成RAII-實施ResNHolder S和寫是這樣的:RAII和構造函數中的異常

void DoTheJob(Logger& log/*, some other params */) { 
    try { 
     Res1Holder r1(/* arguments for creating resource #1 */); 
     try { 
      Res2Holder r2(/* arguments */); 
      DoTheJobQuicklyAndEasily(log, r1, r2); 
     } 
     catch (Res2InitializationException& e) { 
      log.log("Can't obtain resource 2, that'll slowdown us a bit"); 
      DoTheJobWithModerateSuffering(log, r1); 
     } 
    } 
    catch (Res1InitializationException& e) { 
     log.log("Can't obtain resource 1, using fallback"); 
     DoTheJobTheSlowAndPainfulWay(log); 
    } 
} 

「DoTheJobXxx()」採取引用Logger/ResNHolder,因爲它們是不可複製的。我做得太笨拙了嗎?有沒有其他聰明的方法來結構化函數?

+2

我認爲這很好。 – Nawaz

+1

這可以作爲try-catch的教科書示例。 –

+1

我會使用工廠方法返回可選的對象而不是異常。 – yngccc

回答

2

我覺得你的代碼將是不錯,但這裏是一個另類的考慮:

void DoTheJob(Logger &log/*,args*/) 
{ 
    std::unique_ptr<Res1Holder> r1 = acquireRes1(/*args*/); 
    if (!r1) { 
     log.log("Can't acquire resource 1, using fallback"); 
     DoTheJobTheSlowAndPainfulWay(log); 
     return; 
    } 
    std::unique_ptr<Res2Holder> r2 = acquireRes2(/*args*/); 
    if (!r2) { 
     log.log("Can't acquire resource 2, that'll slow us down a bit."); 
     DoTheJobWithModerateSuffering(log,*r1); 
     return; 
    } 
    DoTheJobQuicklyAndEasily(log,*r1,*r2); 
} 

凡acquireRes函數返回一個空的unique_ptr當資源無法初始化:

std::unique_ptr<Res1Holder> acquireRes1() 
{ 
    try { 
    return std::unique_ptr<Res1Holder>(new Res1Holder()); 
    } 
    catch (Res1InitializationException& e) { 
    return std::unique_ptr<Res1Holder>(); 
    } 
} 

std::unique_ptr<Res2Holder> acquireRes2() 
{ 
    try { 
    return std::unique_ptr<Res2Holder>(new Res2Holder()); 
    } 
    catch (Res2InitializationException& e) { 
    return std::unique_ptr<Res2Holder>(); 
    } 
} 
+0

+1雖然問題中的原始代碼是正確的,但這會減少'DoTheJob'函數中的縮進。這是一個有趣的問題,但我認爲這更具可讀性。 –

+0

爲什麼在已經是RAII的資源周圍有unique_ptr? –

+0

@ DieterLucking:使它成爲一個指針可以使資源在各個函數之間有效地傳遞,並且它自然具有一個空值來指示無法獲取該資源。使用'std :: unique_ptr'而不是使用原始指針確保資源自動釋放。 –

1

你的代碼看起來很好,我可以想象你可能會遇到的唯一問題是性能,因爲異常被認爲不是很有效。如果是這樣,你可以更改代碼:

void DoTheJob(Logger& log/*, some other params */) { 
    Res1HolderNoThrow r1(/* arguments for creating resource #1 */); 
    if(r1) { 
     Res2HolderNoThrow r2(/* arguments */); 
     if(r2) 
      DoTheJobQuicklyAndEasily(log, r1, r2); 
     else { 
      log.log("Can't obtain resource 2, that'll slowdown us a bit"); 
      DoTheJobWithModerateSuffering(log, r1); 
     } 
    } else { 
     log.log("Can't obtain resource 1, using fallback"); 
     DoTheJobTheSlowAndPainfulWay(log); 
    } 
} 

你會需要另一個RAII對象不會拋出異常,但有國家和運營商布爾()或其他地方返回。但是你的代碼看起來對我來說更不容易出錯,我寧可使用它,除非你有性能問題或需要避免異常。