Fehlerbehandlung in C++



  • Ich stehe derzeit vor einem mehr oder weniger großen Problem. Wie kann man die Fehlerbehandlung in C++ am schönsten und effizientesten lösen?

    In C ist es häufig sehr einfach gehalten, man erkennt am Rückgabewert einer Funktion die Art des Fehlers. Dies kann man natürlich in C++ ebenfalls so lösen, aber ich denke es gibt auch schönere Wege.

    Die zweite Möglichkeit ist auch noch sehr C-lastig: Die Funktion liefert einen bool zurück und schreibt die Fehlernummer/-meldung in eine Variable.

    Die dritte Art ist typisch C++: Man benutzt Exceptions und die Exception enthält die Fehlerbeschreibung/Fehlernummer. Allerdings sind Exceptions auch nicht immer schön, da 80% aller Funktionen Fehler zurückliefern können. Danach würde mein Sourcecode nur noch voll mit try/catch Blöcken sein. Zudem sollen Exceptions auch teurer sein, als ein einfacher Rückgabewert.

    Wie löst man das in größeren Projekten?

    Danke 🙂



  • erwartest du wunder?

    try/catch sind schon dafür gedacht benutzt zu werden

    für die kleinen sachen benutze ich if



  • Faustregel: Exceptions nur bei Ausnahmen!



  • try schrieb:

    Wie kann man die Fehlerbehandlung in C++ am schönsten und effizientesten lösen?

    .. mit Exceptions. Mittels Exceptions trennt man den Applikations-Code, von dem, der die Fehlerbehandlung macht.

    try schrieb:

    In C ist es häufig sehr einfach gehalten, man erkennt am Rückgabewert einer Funktion die Art des Fehlers. Dies kann man natürlich in C++ ebenfalls so lösen, aber ich denke es gibt auch schönere Wege.

    Die zweite Möglichkeit ist auch noch sehr C-lastig: Die Funktion liefert einen bool zurück und schreibt die Fehlernummer/-meldung in eine Variable.

    Die dritte Art ist typisch C++: Man benutzt Exceptions und die Exception enthält die Fehlerbeschreibung/Fehlernummer. Allerdings sind Exceptions auch nicht immer schön, da 80% aller Funktionen Fehler zurückliefern können. Danach würde mein Sourcecode nur noch voll mit try/catch Blöcken sein.

    typischer Anfänger-Fehler. man sollte sich vorher genau überlegen wo man seine try/catch-Blöcke einfügt. Auch in größeren Projekten müssen das nur zwei bis drei Hierarchie-Stufen sein, um eine vernünftige Fehlerbehandlung zu gewährleisten. D.h. wenn Du in Deinem Projekt eine Aufruf-Struktur von vielleicht 30 hast - dh. Programm ruft Unterprogramm1, das ruft Unterprogramm2 usw. 30mal - so hast Du bei drei Hierachie-Stufen nur in weniger als 10% des Codes try/catch-Blöcke drin.

    try schrieb:

    Zudem sollen Exceptions auch teurer sein, als ein einfacher Rückgabewert.

    Mit der Performance-Keule läßt sich jeder Fortschritt erschlagen 😉

    try schrieb:

    Wie löst man das in größeren Projekten?

    Idealerweise mit Exception.
    Exceptions haben gegenüber den anderen beiden Varianten den Vorteil, dass sie aufgefangen werden müssen. Ich habe schon Mega-Bytes von C-like-Code gesehen, wo immer brav der Fehlercode zurückgegeben wurde, um ihn dann am Ende zu ignorieren.
    Das schwierigste an Exceptions ist es, Exception-sicheren Code zu schreiben. Das ist nicht einfach und man muss sich erstmal daran gewöhnen. Wobei die Reihenfolge gilt: zuerst Exception-sicherer Code und dann erst Exception. Ein wichtiges Idiom (oder Pattern) dafür ist das RAII (Resource Acquisition Is Initialisation).

    Gruß
    Werner



  • Danke Euch! 🙂

    Ich habe aber noch eine tiefergehende Frage zu Exceptions. Wie wird das von den Compilern gelöst, damit auch die Destruktoren im Falle einer Exception aufgerufen werden? Welcher zusätzlicher Bloat wird erzeugt und wo? Nur in der Funktion wo throw aufgerufen wird, oder in der gesamten Aufruf-Struktur? Das würde bei dem obigen Beispiel 30 mal zusätzlichen Code bedeuten, der immer vorhanden ist ob eine Exception geworfen wird oder nicht.



  • Weiß niemand rat? 😞



  • Die Destruktoren müssen auf jeden Fall aufgerufen werden, egal auf welchem Weg der Block der entsprechenden Variablen verlassen wird - d.h. bei einer Exception muß das Programm "nur" alle Destruktoraufrufe abklappern, die es zwischen dem throw und dem zugehörigen catch findet.



  • Objekte auf dem Stack werden autom. frei gegeben und somit auch der Destruktor aufgerufen. Da muß man sich also keine Sorgen machen. Nur bei Heap-Objekten muß man selber ein delete aufrufen (geht ja bei Stackobjekten eh nicht).
    Wenn man Heap-Objekte trotzdem benutzen will, sollte man Smartpointer benutzen, z.B. den auto_ptr.

    try {
       A a; // Stackobjekt wird autom. gelöscht.
       auto_ptr<A> a1(new A());  // wird autom. gelöscht.
       A *a2 = new A();   // muss man selber löschen.
    }catch(...) {
    }
    


  • Wobei für Fälle wie dieser boost::scoped_ptr besser geeignet wäre (so als Ergänzung).
    Zu den Boost Smart-Pointern gibt es auch nen Artikel im C++-Magazin.


Anmelden zum Antworten