Advanced  Services Exceptions Back Next Up Home


Exceptions

Die Ausnahmebehandlung in C++ kennt im Gegensatz zu Java und C# kein dritten finally-Block. Auch die Hierarchie der Exceptions ist hier sehr übersichtlich, auch wenn in C++11 einige Exceptions hinzugekommen sind. Hier zunächst eine Übersicht über die Standardexceptionklassen in C++


Die Hierarchie der Exceptions in C++

cpp-exception-hierarchie.jpg


Werfen mit Standarddatentypen

Der Auslösemechanismus mit throw arbeitet mit Objekten beliebigen Datentyps. Im catch-Zweig des try-catch Konstrukts kann man dann dieses Objekt auswerten. Besonders einfach ist dies mit Standarddatentypen. Die folgenden Beispiel arbeiten nach dem gleichen Muster. Es gibt eine Funktion, die un ter bestimmten Bedingungen eine Ausnahme wirft und es gibt eine weitere Funktion, die diese Funktion verwendet und die Ausnahme abfängt.

Die auslösende Funktion

void openfiles(int filecount)
{
   if ( filecount > 64)
      throw filecount;
}

Die abfangende Funktion

void beispiel1()
{
   try
   {
      openfiles(70);
      cout << "success" << endl;
   }
   catch(int nr)
   {
      cout << "cannot open " << nr << " files, max = 64\n" << endl;
   }
}

Ein Ablauf

cpp-exception-01.jpg

Oft werden c-Strings (char*) eingesetzt.

Die auslösende Funktion

void* allocate(int size)
{
   //void* tmp = calloc(1, size);
   void* tmp = NULL;
   if( tmp == NULL)
      throw "memory allocation failed";
   return tmp;
}

Die abfangende Funktion

void beispiel1()
{
   try
   {
      void* buf = allocate(5000);
      cout << "allocate succeded" << endl;
   }
   catch(char* mess)
   {
      cout << mess << endl << endl;
   }
}

Ein Ablauf

cpp-exception-02.jpg


Werfen einer Exception aus der Exceptionhierarchie

Etwas eleganter ist es Exceptions der Exceptionhierarchie zu verwenden.

Die auslösende Funktion

void* allocate3a(int size)
{
   //void* tmp = calloc(1, size);
   void* tmp = NULL;
   if( tmp == NULL)
   {
      std::ostringstream str;  // #include <sstream>
      str << "cannot allocate memory with size = " << size;
      throw std::bad_alloc( str.str().c_str() );
   }
   return tmp;
}

In jeder Exceptionklasse ist gibt es einen parameterlosen Konstruktor und einen der einen klassischen c-String als Parameter hat. Hat man einen eine andere Stringdarstellung muß man konvertieren.

Die abfangende Funktion

void beispiel3a()
{
   try
   {
      void* buf = allocate3a(5000);
      cout << "allocate succeded" << endl;
   }
   catch(std::bad_alloc ex)
   {
      cout << ex.what() << endl;
   }
}

Statt bad_alloc kann man auch eine Elternklasse verwenden (in diesem Falle ist das std::exception) aber keine Kindklasse.

Ein Ablauf

cpp-exception-03.jpg


Ent-Werfen einer eigenen Exceptionklasse

Eine eigene Exceptionklasse leitet man von einer Standardexceptionklasse ab. Hier ein Beispiel mit dem man über den Konstruktor eine individuelle Fehlermeldung ausgeben kann.

class AllocationException : public exception
{
private:
   char* mess;
public:
   AllocationException() : mess("AllocationException: memory allocation failed")
   {}
   AllocationException(char* mess) : mess(mess)
   {}

  const char *what () const //throw ()
  {
    return mess;
  }
};

Die auslösende Funktion

void* allocate3b(int size)
{
   //void* tmp = calloc(1, size);
   void* tmp = NULL;
   if( tmp == NULL)
      throw AllocationException("Speicher konnte nicht allokiert werden");
   return tmp;
}

In jeder Exceptionklasse ist gibt es einen parameterlosen Konstruktor und einen der einen klassischen c-String als Parameter hat. Hat man einen eine andere Stringdarstellung muß man konvertieren.

Die abfangende Funktion

void beispiel3b()
{
   try
   {
      void* buf = allocate3b(5000);
      cout << "allocate succeded" << endl;
   }
   catch(AllocationException ex)
   {
      cout << ex.what() << endl;
   }
}

Ein Ablauf

cpp-exception-04.jpg


Mehrere catch-Blöcke

Es kann ohne weiteres mehrere catch-Blöcke geben. Stehen die Exceptionklassen nicht in direkter Vererbung ist die Reihenfolge der catch-Blöcke egal, andernfalls muß die Reihenfolge von der Kindklasse zur Elternklasse sein. Als letzten catch-Block kann man immer catch(...) nehmen, damit fängt man unbekannte Fehler ab. Hier eine mögliche Abfolge von catch-Blöcken. Damit kann man für jede Exception eine eigene Fehlerbehandlung erreichen.

void beispiel3c()
{
   try
   {
      void* buf = allocate3b(5000);
      cout << "allocate succeded" << endl;
   }
   catch(AllocationException ex)
   {
      cout << ex.what() << endl << endl;
      // action 1
   }
   catch(exception ex)
   {
      cout << ex.what() << endl << endl;
      // action 2
   }
   catch(...)
   {
      cout << "unknown exception caught" << endl << endl;
      // action 3
   }
}

Valid XHTML 1.0 Strict top Back Next Up Home