====== Exceptions in der Standardbibliothek ====== ===== std::exception ===== In der C++ Standardbibliothek werden eine Reihe von Exceptions verwendet. Hierbei gibt es eine [[cpp:class:start|Klasse]] 'std::exception' von der alle Exceptions [[cpp:inheritance:start|abgeleitet]] sind, die wie folgt definiert ist ((Quelle: //Working Draft, Standard for Programming Language C++//. 2005-10-19, Kapitel 18.6.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. ===== Vererbungshierarchie ===== Die Vererbungshierarchie von std::exception ist wie folgt aufgebaut: * std::exception * [[#stdbad_alloc|std::bad_alloc]] * [[#stdbad_cast|std::bad_cast]] * std::bad_exception * std::bad_typeid * std::ios_base::failure * std::logic_error * std::domain_error * std::invalid_argument * std::length_error * std::out_of_range * [[#stdruntime_error|std::runtime_error]] * std::overflow_error * std::range_error * std::underflow_error 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: ==== std::bad_alloc ==== 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 '' einbinden. ==== std::bad_cast ==== Wie der Name schon vermuten lässt wird ''std::bad_cast'' bei einem fehlgeschlagenem [[cpp:cast:start|Cast]] geworfen. Genauer genommen passiert das nur bei einem [[cpp:cast:dynamic|dynamic_cast]], da dieser der einzige Cast ist, der zur Laufzeit erfolgt. Und auch hier nur bei der Verwendung von [[cpp:ref:start|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(ref_d1); // Wirft hoffentlich eine Exception... } catch(std::bad_cast& ex) { std::cout << "dynamic_cast fehlgeschlagen!" << std::endl; } ==== std::runtime_error ==== 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 try { throw std::runtime_error("Ganz blöd!"); //... } catch(std::exception& ex) { std::cout << ex.what() << std::endl; } ===== Eigene Exceptions ===== 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 [[http://www.boost.org/doc/libs/1_42_0/libs/exception/doc/boost-exception.html|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. \\ ---- ← **[[cpp:exception:usage-syntax|Exceptionbehandlung in C++]]** ← | ↑ **[[cpp:exception:start|Exceptions Start]]** ↑ | → **[[cpp:exception:how|Was passiert wenn eine Exception auftritt?]]** →\\ [[http://forum.proggen.org/viewtopic.php?f=39&t=1504|Diskussion]]