In der C++ Standardbibliothek werden eine Reihe von Exceptions verwendet. Hierbei gibt es eine Klasse 'std::exception' von der alle Exceptions abgeleitet sind, die wie folgt definiert ist 1):
namespace std { class exception { public: exception() throw(); exception(const exception&) throw(); exception& operator=(const exception&) throw(); virtual ~exception() throw(); virtual const char* what() const throw(); }; }
Was hier auffällt ist das nach jeder Methode mittels 'throw()' spezifiziert wird das diese keine Exception wirft. Damit soll sichergestellt werden das während eine geworfene Exception behandelt wird keine weitere geworfen wird, da dies zu einem sofortigen Beenden des Programms führen würde. Sollte aus einer der Methoden trotzdem eine Exception fliegen wird das Programm ebenfalls beendet. Wie das genau abläuft ist an dieser Stelle noch nicht relevant und werden wir erst später betrachten.
Die wohl wichtigste Methode ist 'what()' welche eine kurze, meist generische Beschreibung des aufgetretenen Fehlers zurück gibt. Diese Methode ist virtuell und kann von abgeleiteten Klassen überschrieben werden, um eigenen Fehlerbeschreibungen zurück zu geben.
Die Vererbungshierarchie von std::exception ist wie folgt aufgebaut:
Um die beiden letzten Klassen von Exceptions, also std::logic_error und std::runtime_error mit ihren Erben verwenden zu können müssen wir den Header stdexcept
einbinden.
Im Folgenden betrachten wir kurz die wichtigsten Klassen:
Mit malloc
hat man früher immer den Rückgabewert auf NULL
überprüfen müssen, um festzustellen ob die Speicheranforderung funktioniert hat. Mit C++ und new
hat sich das geändert, da hier bei einer fehlgeschlagenen Allokierung anstatt NULL
zurückzugeben eine Exception vom Typ std::bad_alloc geworfen wird.
try { char* mem = new char[size]; // Wenn man hier angelangt hat die Speicheranforderung sicher funktioniert } catch(std::bad_alloc& ex) { std::cout << "Speicheranforderung fehlgeschlagen!" << std::endl; }
Möchte man weiterhin anstatt mit einer Exception, durch die Rückgabe von NULL
über eine fehlgeschlagenen Speicheranforderung benachrichtigt werden kann man dies durch die Angabe von std::nothrow
als Argument von new
erreichen.
char* mem = new(std::nothrow) char[size]; if( !mem ) { std::cout << "Speicheranforderung fehlgeschlagen!" << std::endl; }
Sollte der Compiler std::nothrow
nicht kennen kann man die Definition mit Hilfe von #include <new>
einbinden.
Wie der Name schon vermuten lässt wird std::bad_cast
bei einem fehlgeschlagenem Cast geworfen. Genauer genommen passiert das nur bei einem dynamic_cast, da dieser der einzige Cast ist, der zur Laufzeit erfolgt. Und auch hier nur bei der Verwendung von Referenzen. Bei der Verwendung von Zeiger wird immer NULL
zurück gegeben.
struct Base { virtual ~Base() {} // dynamic_cast ist nur bei polymorphen Klassen möglich }; struct Derived1: public Base {}; struct Derived2: public Base {}; Derived1 d1; Base& ref_d1 = d1; try { Derived2& ref_d2 = dynamic_cast<Derived2&>(ref_d1); // Wirft hoffentlich eine Exception... } catch(std::bad_cast& ex) { std::cout << "dynamic_cast fehlgeschlagen!" << std::endl; }
Diese Klasse stellt noch eine Besonderheit dar, da sie einen Konstruktor bereit stellt, der einen std::string
als Argument erwartet und den angegebenen String als Rückgabewert von std::exception::what()
bereitstellt. Damit eignet sie sich gut um eine Exception zu werfen ohne dafür einen neuen Typ zu definieren und dabei trotzdem Informationen zum Fehler bereit zu stellen.
#include <stdexcept> try { throw std::runtime_error("Ganz blöd!"); //... } catch(std::exception& ex) { std::cout << ex.what() << std::endl; }
Die vordefinierten Exceptions sind alle sehr allgemein gehalten. Für den eigenen Bedarf hat es sich deshalb bewährt von möglichst passenden Exceptions in der Hierarchie eigene abzuleiten und mit weiteren Informationen auszustatten. Wer bereits etwas Erfahrungen mit Exceptions hat sollte sich auch Boost Exception anschauen, da es auch das Hinzufügen von Informationen auf verschiedenen Ebenen erlaubt und so auch erlauben den Fehler leichter zurück zu verfolgen.
← Exceptionbehandlung in C++ ← | ↑ Exceptions Start ↑ | → Was passiert wenn eine Exception auftritt? →