2013-09-25 153 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对象不会抛出异常,但有国家和运营商布尔()或其他地方返回。但是你的代码看起来对我来说更不容易出错,我宁可使用它,除非你有性能问题或需要避免异常。