2013-09-25 5 views
7

Stellen Sie sich vor, ich hätte eine Aufgabe, die auf drei verschiedene Arten erledigt werden kann: eine langsame und schmerzhafte, aber ausfallsichere Art; der Weg mit mäßigem Leiden, vorausgesetzt, Sie haben Resource1; und ein schneller und einfacher Weg, der sowohl Resource1 als auch Resource2 erfordert. Nun sind diese Ressourcen wertvoll, damit ich sie einpacken in RAH-Implementierung ResNHolder s und schreiben dies so etwas wie:RAII und Ausnahme im Konstruktor

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()“ nehmen Hinweise auf Logger/ResNHolder, denn sie sind nicht kopierbar. Tue ich es zu ungeschickt? Gibt es eine andere clevere Möglichkeit, die Funktion zu strukturieren?

+2

I denke, das ist in Ordnung. – Nawaz

+1

Dies könnte sub-als ein Lehrbuch Beispiel für Try-Catch. –

+1

Ich würde eine Factory-Methode verwenden, die ein optionales Objekt anstelle der Ausnahme zurückgibt. – yngccc

Antwort

2

Ich denke, Ihr Code wäre ganz gut, aber hier ist eine Alternative zu betrachten:

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

Wo die acquireRes Funktionen einen Null-unique_ptr zurück, wenn die Ressource nicht initialisiert werden:

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 Während der ursprüngliche Code in der Frage korrekt ist, reduziert dies den Einzug in der 'DoTheJob'-Funktion. Es ist eine Frage des Geschmacks, aber ich denke, das ist lesbarer. –

+0

Warum gibt es ein unique_ptr um eine Ressource, die bereits RAII ist? –

+0

@ DieterLücking: Wenn Sie einen Zeiger verwenden, kann die Ressource zwischen den Funktionen effizient übertragen werden, und sie hat natürlich einen Nullwert, um anzuzeigen, dass die Ressource nicht erfasst werden konnte. Die Verwendung von 'std :: unique_ptr' im Gegensatz zur Verwendung eines rohen Zeigers stellt sicher, dass die Ressource automatisch freigegeben wird. –

1

Sie Code sieht gut aus, das einzige Problem, das ich vorstellen könnte, dass Sie mit der Leistung haben, als Ausnahme als nicht sehr effektiv. Wenn dies der Fall können Sie den Code ändern:

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

würden Sie brauchen eine andere RAH-Objekt, das nicht Ausnahme wirft aber hat Zustand und gibt es in Operator bool() oder woanders. Aber Ihr Code sieht weniger fehleranfällig für mich aus, und ich würde es lieber verwenden, wenn Sie kein Performance-Problem haben oder Ausnahmen vermeiden müssen.