Exception hierarchies

In object-oriented programming languages the main technique for expressing specialization and generalization is the inheritance hierarchy. Inheritance in exceptions are particularry important for groupping exceptions in catch handlers.



try {
    ...
}
catch( Der1 d1 ) { ... }
catch( Der2 d2 ) { ... }
catch( Der3 d3 ) { ... }
catch( Base b )  { ... }


// example1:

class net_error { ... };
class file_error { ... };

class nfs_error : public net_error, public file_error { ... };


void f()
{
    try
    {
        ...
    }
    catch( nfs_error nfs ) { ... }
    catch( file_error fe ) { ... }
    catch( net_error ne )  { ... }
}




// example2:



#ifndef MATRIX_H
#define MATRIX_H

#include <string>
#include <sstream>
#include <stdexcept>

struct matrixError
{
    matrixError( std::string r) : reason(r) { }
    const std::string reason;
};
struct indexError : public matrixError, public std::out_of_range
{
    indexError( int i, const char *r="Bad index") :
                  matrixError(r), out_of_range(r), index(i) { }
    const char *what() const throw()
    {
        std::ostringstream os;
        os << std::out_of_range::what();
        os << ", index = " << index;
        return os.str().c_str();
   }
   virtual ~indexError() throw () { }
   int index;
};
struct rowIndexError : public indexError
{
    rowIndexError(int i) : indexError( i, "Bad row index") { }
};
struct colIndexError : public indexError
{
    colIndexError(int i) : indexError( i, "Bad col index") { }
};

template <class T>
class matrix
{
public:
       matrix( int i, int j );
       matrix( const matrix &other);
       ~matrix();
    matrix operator=( const matrix &other);

    int rows() const { return x; }
    int cols() const { return y; }

    T& at( int i, int j) throw(indexError);
    T  at( int i, int j) const throw(indexError);
    T& operator()( int i, int j);
    T  operator()( int i, int j) const;

    matrix operator+=( const matrix &other);

private:
    int  x;
    int  y;
    T   *v;
    void copy( const matrix &other);
    void check( int i, int j) const throw(indexError);
};

template <class T>
matrix<T>::matrix( int i, int j)
{
    x = i;
    y = j;
    v = new T[x*y];
}
template <class T>
matrix<T>::matrix( const matrix &other)
{
    copy( other);
}
template <class T>
matrix<T>::~matrix()
{
    delete [] v;
}
template <class T>
matrix<T> matrix<T>::operator=( const matrix &other)
{
    if ( this != &other )
    {
        const int oldx = x;
        const int oldy = y;
        const T  *oldv = v;
        try {
            copy( other);
            delete [] oldv;
        }
        catch(...) {
            x = oldx;
            y = oldy;
            delete [] v;
            v = oldv;
            throw;
        }
    }
    return *this;
}
template <class T>
void matrix<T>::copy( const matrix &other)
{
    x = other.x;
    y = other.y;
    v = 0;
    v = new T[x*y];
    for ( int i = 0; i < x*y; ++i )
        v[i] = other.v[i];
    // possible memory leak at v
}
template <class T>
void matrix<T>::check( int i, int j) const throw( indexError )
{
    if ( i  < 0  ||  i >= x )
        throw rowIndexError(i);
    if ( j  < 0  ||  j >= y )
        throw colIndexError(j);
}
template <class T>
T& matrix<T>::at( int i, int j) throw(indexError)
{
    check(i,j);
    return operator()(i,j);
}
template <class T>
T matrix<T>::at( int i, int j) const throw( indexError)
{
    check(i,j);
    return operator()(i,j);
}
template <class T>
T& matrix<T>::operator()( int i, int j)
{
    return v[i*y + j];
}
template <class T>
T matrix<T>::operator() ( int i, int j) const
{
    return v[i*y + j];
}
template <class T>
matrix<T> matrix<T>::operator+=( const matrix &other)
{
    for ( int i = 0; i < x*y; ++i )
        v[i] += other.v[i];
    return *this;
}
template <class T>
matrix<T> operator+( const matrix<T> &left, const matrix<T> &right)
{
    matrix<T> temp(left);
    temp += right;
    return temp;
}

#endif /* MATRIX_H */