2013-09-25 2 views
7

Представьте, что у меня есть работа, которую можно выполнить тремя способами: медленным и болезненным, но безотказным способом; путь с умеренными страданиями, учитывая, что у вас есть Resource1; и быстрый и простой способ, который требует как Resource1, так и Resource2. Теперь эти ресурсы являются ценными, так что я оберните их в RAII реализующее ResNHolder с и написать что-то вроде этого: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

I думаю, что это нормально. – Nawaz

+1

Это может быть sub как пример учебника для 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

Почему существует уникальный_ptr ресурс, который уже RAII? –

+0

@ DieterLücking: включение указателя позволяет эффективно передавать ресурс между функциями, и, естественно, имеет нулевое значение, указывающее, что ресурс не может быть получен. Использование '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, который не выбрасывает исключение, но имеет состояние и возвращает его в операторе BOOL() или где-нибудь еще. Но ваш код выглядит менее подверженным ошибкам, и я бы предпочел использовать его, если у вас нет проблемы с производительностью или нужно избегать исключений.

Смежные вопросы