Exceptions

Exceptions - zu deutsch 'Ausnahmen' sind die C++ Methode um mit Fehlern umzugehen.

    double divide(int divident, int divisor)
    {
      if(divisor==0)
      {
        throw "Divisor must not be zero";
      }
      return static_cast<double>(divident)/divisor;
    }

Wenn der Divisor 0 ist, wird eine Exception geworfen. Auf Exception reagiert man mittels try/catch Blöcken:

    try
    {
      cout<<divide(2,3);
    }
    catch(char const* e)
    {
      cout<<"failure: "<<e;
    }

Der Vorteil von Exception ist, dass man ganz woanders auf den Fehler reagieren kann, als er ausgelöst wurde.

    void g()
    {
      throw 3;
    }
    void f()
    {
      g();
    }
    void s()
    {
      f();
    }

    int main()
    {
      try
      {
        s();
      }
      catch(int e)
      {
        cout<<"Fehler";
      }
    }

Der Fehler tritt in der Funktion g auf, aber wird erst in der main Funktion behandelt. Dies bedeutet, dass wir dort auf den Fehler reagieren können, wo wir wollen und nicht dort wo er auftritt. Mittels throw kann man alles mögliche werfen - meistens wirft man aber eine Instanz einer Exceptionklasse: throw DivideByZeroException();

Exception sind auch die einzige Möglichkeit einen Konstruktor fehlschlagen zu lassen - denn ein Konstruktor liefert ja keinen Wert zurück. Tritt ein Fehler im Konstruktor auf, wird das Objekt nicht erstellt und somit auch nicht der Destruktor aufgerufen. Wichtig ist, dass der Destruktor jeden Objektes aufgerufen wird, wenn eine Exception geworfen wird.

Mittels catch() können wir eine Exception fangen. Wir können catch(Exception& e) schreiben um alle davon public abgeleiteten Klassen ebenfalls zu fangen.

Merksatz!!Eine Exception wird immer by value geworfen (throw Exception();) und by reference gefangen (catch(Exception& e)).

Wenn wir nicht wissen was für eine Exception geworfen wurde, können wir catch(...) alles fangen. Der Nachteil ist natürlich, dass wir nicht gut reagieren können, weil wir den Typ der Exception trotzdem nicht kennen.

Die catch-Blöcke werden von oben nach unten durchgegangen, wenn wir also die Basisklasse ganz oben fangen und unten erst die Abgeleitete, dann wird die Exception immer als Basisklasse behandelt.

Mit der Funktion uncaught_exception() aus <exception> kann man feststellen, ob gerade eine Exception aktiv ist. Dies kann man zB zum loggen von Fehlern verwenden:

    #include<iostream>
    #include<exception>
    using namespace std;

    class Logger
    {
    public:
      ~Logger()
      {
        if(uncaught_exception())
        {
          cout<<"Funktion wurde durch einen Fehler beendet\n";
        }
      }
    };

    void foo()
    {
      Logger log;
      throw "Fehler";
    }

    int main()
    {
      try
      {
        foo();
      }
      catch(...)
      {
      }
    }
    

top