Exceptions

Fehlerbehandlung in C++
In allen richtigen Programmen spielt die Behandlung von Fehlern eine zentrale Rolle.

Programme sollen robust sein, d.h. sie sollen falsche Eingaben, extreme Datenwerte oder generell unerwartete Situationen vernünftig abfangen. Das Programm soll weder ausser Kontrolle in einem undefinierten Zustand enden noch bei jeder Kleinigkeit abgebrochen werden.

Beispiel:



double fak( int n )
{ // Funktion zur Berechnung der Fakultaet
  double t = 1.;
  for ( int i = 2; i <= n; i++ ) {
    t *= i;
  }
  return(t);
}



Funktion völlig korrekt, verlässt sich aber darauf dass Funktion richtig verwendet wird, d.h. dass Eingabewerte bestimmte Bedingungen erfüllen ( n >= 0, n < NMAX). Unzureichend für allgemeine Anwendungen, keine production quality, Überprüfung und ggf Aktion nötig.

Klassisch gibt es 2 Methoden:
Die sanfte mittels Rückgabewert und if Abfragen:



double fak( int n )
{ // Funktion zur Berechnung der Fakultaet
  if ( n < 0 ) {
    cout << "Error in fak: Argument < 0 " << n << endl;
    return(-1.);
  }
  if ( n > NMAX ) {
    cout << "Error in fak: Argument > NMAX " << n<< endl;
    return(-2.);
  }
  double t = 1.;
  for ( int i = 2; i <= n; i++ ) {
    t *= i;
  }
  return(t);
}


Nachteile:


2. Alternative, die brutale mittels exit() call bei Fehler. Definiertes Verhalten, allerdings keine Fehlerbehandlung im laufenden Programm mehr möglich.



double fak( int n )
{ // Funktion zur Berechnung der Fakultaet
  if ( n < 0 ) {
    cout << "Error in fak: Argument < 0 " << n << endl;
    exit(1);
  }
  if ( n > NMAX ) {
    cout << "Error in fak: Argument > NMAX " << n<< endl;
    exit(2);
  }
  double t = 1.;
  for ( int i = 2; i <= n; i++ ) {
    t *= i;
  }
  return(t);
}


Beispiel File-Öffnen, sinnvoll je nach Situation:


Wesentlich flexibler und eleganter in C++ mit exceptions (=Ausnahmen).



#include <vector>  // vector headers
#include <iostream>
#include <cmath>
#include <stdexcept>

using namespace std;

double root(double A, double B, double C) 
{
  // Returns the larger of the two roots of
  // the quadratic equation A*x*x + B*x + C = 0.
  // (Throws an exception if A == 0 or B*B-4*A*C < 0.)
  if (A == 0) {
    throw ("A can't be zero.");
  }
  else {
    double disc = B*B - 4*A*C;
    if (disc < 0)
      throw ("Discriminant < zero.");
    return  (-B + sqrt(disc)) / (2*A);
  }
}
int main()
{
  //  cout << root( 0., 1., 1.) << endl;
  try {
     cout << root( 0., 1., 1.) << endl;
  }
  catch (char const *message ) {
    cout << "root failed:  " << message << endl;  // print error
     
  }   
  try {
     cout << root( 1., 2., 1.5) << endl;
  }
  catch (char const *message ) {
    cout << "root failed:  " << message << endl;  // print error
 
  }   
}




Ähnlich wie Funktionsaufruf: throw( Type a ) bewirkt Verzweigung nach passendem catch ( Type x).

if Abfragen vs Exceptions ist klassisch–philosophisches IT–Problem, siehe z.B. LBYL vs EAFP.


Leider ist das exception–Konzept erst nachträglich in C++ aufgenommen worden

Beispiel mit STL Vektoren:



#include <vector>  // vector headers
#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
   vector< double > v(10,1.); // <double> vector, 10 elements filled with 1
   cout << v[5] << endl;  // ok, exists
   cout << v[15] << endl; // doesn't exist but program continues

   cout << v.at(5) << endl;  // ok
   cout << v.at(15) << endl; // doesn't exist, throws exception, abort

   try {
     cout << v.at(15) << endl; // doesn't exist, throws exception, catched
   }
   catch (out_of_range e) {  // pre-defined logical exception class is thrown
     cerr << "Out of range exception " << e.what() << endl;  // print error
     exit(1);  // stop program or take other action
   }   
}



Anders in JAVA oder Python, exceptions sind integraler Bestandteil der Sprache: I/O, array bounds, mathematische Operationen, ...