2010-05-02 7 views
5

Ich versuche zwischen zwei Möglichkeiten der Instanziierung eines Objekts & zu entscheiden, das alle Konstruktorausnahmen für ein für mein Programm kritisches Objekt behandelt, dh wenn die Konstruktion fehlschlägt, kann das Programm nicht fortgesetzt werden .Guter Stil für den Umgang mit Konstruktorfehler des kritischen Objekts

Ich habe eine Klasse SimpleMIDIOut, die grundlegende Win32 MIDI-Funktionen umschließt. Es öffnet ein MIDI-Gerät im Konstruktor und schließt es im Destruktor. Es wird eine Exception ausgelöst, die von std :: exception im Konstruktor geerbt wird, wenn das MIDI-Gerät nicht geöffnet werden kann. wäre im Einklang mit C++ Best Practices

Methode 1

Welche der folgenden Möglichkeiten, für diesen Objektkonstruktor Ausnahmen fangen - Stapel reserviert Objekt, nur in ihrem Umfang innerhalb try-Block

#include <iostream> 
#include "simplemidiout.h" 

int main() 
{ 
    try 
    { 
     SimpleMIDIOut myOut; //constructor will throw if MIDI device cannot be opened 
     myOut.PlayNote(60,100); 

     //..... 
     //myOut goes out of scope outside this block 
     //so basically the whole program has to be inside 
     //this block. 
     //On the plus side, it's on the stack so 
     //destructor that handles object cleanup 
     //is called automatically, more inline with RAII idiom? 
    } 
    catch(const std::exception& e) 
    { 
     std::cout << e.what() << std::endl; 
     std::cin.ignore(); 
     return 1; 
    } 

    std::cin.ignore(); 
    return 0; 
} 

Methode 2 - Zeiger auf Objekt, Heap allokiert, schöner strukturierter Code?

#include <iostream> 
#include "simplemidiout.h" 

int main() 
{ 
    SimpleMIDIOut *myOut; 

    try 
    { 
     myOut = new SimpleMIDIOut(); 
    } 
    catch(const std::exception& e) 
    { 
     std::cout << e.what() << std::endl; 
     delete myOut; 
     return 1; 
    } 

    myOut->PlayNote(60,100); 

    std::cin.ignore(); 

    delete myOut; 
    return 0; 

} 

Ich mag das Aussehen des Codes in Methode 2 besser, nicht über mein ganzes Programm in einen try-Block stauen, aber Methode 1 erstellt das Objekt auf dem Stapel so C++ die Lebensdauer des Objekts verwaltet, Das passt besser zur RAII-Philosophie, oder?

Ich bin immer noch ein Neuling in diesem so jede Rückmeldung über die oben genannten wird sehr geschätzt. Wenn es einen noch besseren Weg gibt, den Konstruktorfehler in einer solchen Situation zu prüfen/behandeln, lass es mich wissen.

+0

+1 für eine gut gestellte erste Frage. – razlebe

Antwort

2

Ich persönlich ziehe die erste Art, die Sie verwendet haben - Methode 1 - dass das SimpleMIDIOut Objekt als lokal auf dem Umfang der Try-Catch-Block zuzuordnen.

  • Für mich einer der Vorteile eines Try-Catch-Block ist, dass eine saubere, ordentliche Anlage für die Fehlerbehandlung Code liefert - der Block Fang -, dass Sie Ihre Geschäftslogik in einem schön angeben können, , lesbarer, ununterbrochener Fluss. Zweitens ist der try-catch-Block, den Sie angegeben haben, generisch genug, um jede Ausnahme zu behandeln, die sich von std::exception ableitet - also ist es nicht schlimm, den Großteil Ihres Programmcodes innerhalb des try-catch-Blocks zu haben. Dadurch wird verhindert, dass Ihr Programm unerwartet beendet wird, wenn das Schlimmste eintritt und eine Ausnahme ausgelöst wird. Möglicherweise könnte dies eine Ausnahme sein, die Sie nicht erwarten, wie ein Index außerhalb der Grenzen oder eine Speicherzuordnungsausnahme.

  • Wenn Sie aus Lesbarkeitsgründen nicht viel Code im try-catch-Block haben möchten, ist das in Ordnung, weil es eine gute Design-Praxis ist, refactor big lumps of code into functions. Auf diese Weise können Sie die Hauptfunktion selbst auf eine minimale Anzahl von Zeilen beschränken.

bei Methode der Suche 2:

  • Sie, dass delete im catch-Block nicht brauchen. Wenn während der Konstruktion eine Ausnahme ausgelöst wurde, gibt es dort kein Objekt, das gelöscht werden könnte.

  • Haben Sie darüber nachgedacht, wie Sie für alle std::exception, die von myOut->PlayNote geworfen werden könnte, planen?Es liegt außerhalb des Geltungsbereichs von try-catch, sodass eine Ausnahme hier das Programm unerwartet beendet. Das ist es, worüber ich mit meiner zweiten Kugel oben hinausgekommen bin.

Wenn Sie den größten Teil des Programms zu wickeln, zu entscheiden, waren in diesem Try-catch-Block, möchte aber nach wie vor dynamisch die SimpleMIDIOut Objekt zuordnen, können Sie die Speicherverwaltung ein wenig erleichtern, indem sie ein auto_ptr mit zu verwalten Sie den Speicher im Fall einer Ausnahme:

try 
{ 
    std::auto_ptr<SimpleMIDIOut> myOut(new SimpleMIDIOut()); 
    myOut->PlayNote(60,100); 
    std::cin.ignore(); 
} // myOut goes out of scope, SimpleMIDIOut object deleted 
catch(const std::exception& e) 
{ 
    std::cout << e.what() << std::endl; 
    return 1; 
} 


return 0; 

... aber Sie können auch erstellen, das SimpleMIDIOut Objekt als lokales anstatt dynamisch.

+0

Danke für die ausführliche Antwort. Denkanstoß. Das alles in diesen try/catch-Block zu stecken, erscheint mir auf einer ästhetischen Ebene komisch und ich wollte ein wenig eine Plausibilitätsprüfung dieser Code-Struktur durchführen. – MTLPhil

+0

Es mag seltsam klingen, aber es ist in der Tat eine sehr gute Methode, den Code in 'main' in einen try/catch-Block zu legen, um eine unerwartete Beendigung zu verhindern (und die Ausnahme zur Analyse zu protokollieren). Ich sollte jedoch beachten, dass es eine Art verzweifelter Maßnahme ist und nur für "Haupt-" und Haupt-ähnliche Funktionen gilt, nehmen Sie nicht die Gewohnheit an, jeden Funktionskörper in einen Versuch/Fang einzubinden;) –

1

Können Sie klären, ob Sie Kontrolle über den Quellcode in SimpleMIDIOut haben?

Wenn Sie das tun, sollte diese Klasse keine Ausnahme vom CTOR werfen. Der Code im CTOR dieser Klasse sollte mit einem try \ catch-Block umschlossen werden.

Wenn Sie nicht dann würde ich mit Methode 1 für Klarheit gehen - die Tatsache, dass das gesamte Programm in diesem Versuch Block sein muss, kann in kleine Methoden Refaktorierung gelöst werden.

+1

Es ist vollkommen gültig, eine Ausnahme zu werfen von einem Konstruktor, um dem Aufrufer anzuzeigen, dass die Konstruktion aus irgendeinem Grund nicht abgeschlossen werden konnte. – razlebe

+0

Es ist gültig - aber meiner Meinung nach ist es schlechtes Design, das zu Laufzeitausnahmen führen könnte –

+0

@drelihan Die Alternative besteht darin, die Konstruktion eines Objekts in einem unbrauchbaren Zustand zu erlauben und dann darauf zu warten, dass es irgendwo benutzt wird. Eine viel schlechtere Designwahl IMHO. – razlebe

Verwandte Themen