Programación en C++/Excepciones

De Wikilibros, la colección de libros de texto de contenido libre.

Editores:
Oscar E. Palacios

Plantillas Librería Estándar de Plantillas

Tabla de contenidos

[editar] Control de excepciones

Una excepción es un error que puede ocurrir debido a una mala entrada por parte del usuario, un mal funcionamiento en el hardware, un argumento inválido para un cálculo matemático, etc. En fin, el programador debe estar atento y escribir los algoritmos necesarios para evitar a toda costa que un error de excepción pueda hacer que el programa se interrumpa de manera inesperada. C++ soporta una forma más elegante para el manejo de excepciones que su contraparte el C estándar y esta consiste en el mecanismo try, throw y catch. La lógica del mecanismo mencionado consiste en:

  1. Dentro de un bloque try se pretende evaluar una o más expresiones y si dentro de dicho bloque se produce un error se lanza por medio de throw la excepción, misma que deberá ser capturada por un catch específico.

  2. Puesto que desde un bloque try pueden ser lanzados diferentes tipos de errores de excepción es que puede haber más de un catch para capturar a cada uno de los mismos.

  3. Si desde un try se lanza una excepción y no existe el mecanismo catch para tratar dicha excepción el programa se interumpirá abruptamente despues de haber pasado por todos los catchs que se hayan definido y de no haber encontrado el adecuado.

  4. Los tipo de errorres lazados pueden ser de un tipo primitivo tal como: int, float, char, etc. aunque normalmente las exepciones son lanzadas por alguna clase escrita por el usuario o por una clase de las que vienen incluidas con el compilador.

En el programa que se listará en seguida se muestra un ejemplo de como lanzar una excepción de tipo int dentro del bloque try, y como capturar el error por medio de catch.

// Demostración: try, throw y catch
#include <iostream>
 
using namespace std;
 
int main()
{
    try {
        throw 125;
        }
    catch(int) {
        cout << "Error tipo entero capturado" << endl;
        }
 
    cin.get();
    return 0;
}

[editar] Excepciones genéricas

Como ya hemos mencionado, los errores pueden deberse a una multitud de situaciones muchas veces inesperadas, por tal motivo, en C++ existe una forma de manejar excepciones desconocidas ( genéricas ) y es buena idea que si usted esta escribiendo un controlador de errores incluya un catch para capturar errores inesperados. Por ejemplo, en el siguiente progama se escribe un catch que tratará de capturar cualquier error inesperado. Observe que la sintaxis para declarar un catch general es:

catch(...) { bloque de imstrucciones }
// Demostración: try, throw y catch
#include <iostream>
 
using namespace std;
 
int main()
{
    try {
        throw 125;
        }
    catch(...) {
        cout << "Ha ocurrido un error inesperado..." << endl;
        }
    cin.get();
    return 0;
}

[editar] Excepciones de clases

Si usted está usando una clase escrita por terceras personas o de las que se incluyen con el compilador y desea utilizar el mecanismo try, deberá conocer el tipo de excepción lanzado por dicha clase para así poder escribir el catch correspondiente para el tratamiento de dicho error. Por ejemplo, la función at() de la clase string ( que será estudiada más adelante ) lanza una excepción cuando se trata de leer o escribir un componente fuera de rango. En tales casos usted puede proceder a capturar el error como se muestra en el siguiente programa.

// Compilado y probado exitosamente con Dev-C++
// Demostración: excepción de la clase string
 
#include <iostream>
#include <string>
 
using namespace std;
 
int main()
{
    string s = "Hola";
 
    try {
        cout << s.at(100) << endl;
        }
    catch(exception& e) {
        cout << e.what() << endl;
    }
 
    cin.get();
    return 0;
}

En el programa anterior el método at de la clase string lanzará ( throw-up ) un error de excepción, ya que la instrucción s.at(100) trata de acceder a un elemento fuera de los limites del objeto ( s ).

[editar] La clase exception

Tal como se mostró en el programa anterior, los errorres generados por las librerías estándar de C++ pueden ser capturados por un catch que tome un parámetro tipo exception. Realmente, exception es una clase base de donde usted puede derivar las suyas y sobrescribir los métodos para el tratamiento de excepciones. La clase exception está incluida en la libreria <exception> y su estructura es la siguiente:

class exception {
public:
    exception() throw() { }
    virtual ~exception() throw();
    virtual const char* what() const throw();
};

En muchos casos bastará con sobrescribir el método what() en la clase derivada de exception, ya que dicho método es el encargado de generar el mensaje que trata de explicar la naturaleza del error ocurrido. En el programa que veremos en seguida, se da un ejemplo sencillo de cómo crear una clase derivada de la clase exception con el objetivo de sobrescribir el método what().

// Demostración: sobrescritura del método what()
#include <iostream>
#include <cstdlib>
#include <exception>
 
using namespace std;
 
class div_cero : public exception
{
public:
    const char* what() const throw()
    {
        return "Error: división por cero...";
    }
};
 
int main(int argc, char *argv[])
{
    double N, D;
    cout << "Probando división" << endl;
    cout << "Ingrese el numerador :";
    cin  >> N;
    cin.clear();
 
    cout << "Ingrese el denominador :";
    cin  >> D;
    cin.clear();
 
    try {
        if (D == 0) throw div_cero();
        cout << N << " / " << D << " = " << N/D << endl;
        }
    catch(div_cero& e) {
        cout << e.what() << endl;
    }
 
    system("PAUSE");
    return 0;
}

Siguiendo la misma metodología mostrada por el programa anterior, usted puede crear clases independientes para capturar errores de excepciones específicas. Por ejemplo, si se desea crear una serie de funciones matemáticas se deben considerar los sucesos de errores tales como: División por cero, Error de dominio, Error de rango, etc. Así, el siguiente programa puede ser un buen ejemplo para que usted escriba sus propios controladores de mensajes de error. Observe cómo en el programa se crea la clase ErrorMat y dentro de la misma la función porque() la cual se encargará de desplegar el mensaje de error. Aunque ErrorMat solo ha sido pensada para tratar los posibles errores de rango y errores de dominio, la misma puede rescribirse para capturar todos los errores posibles que puedan resultar a raiz de operaciones matemáticas.

Nota: No deje de observar también, cómo la función independiente logaritmo() verifica si el parámetro pasado a la misma es 0 o menor que 0 y en tales circunstancias se lanzaría (throw) un error de excepción del tipo ErrorMat ya que el dominio para la función log() es el de los números positivos y el logaritmo de cero no está definido en los números reales.

// Demostración: try, throw y catch
<pre>
#include <iostream>
#include <cmath>
 
using namespace std;
 
#define EDOMINIO 100
#define ERANGO   101
 
class ErrorMat
{
public:
    errormat() : motivo(0) {};
    errormat(int m) : motivo(m) {};
    const char* porque() const throw();
 
private:
    int motivo;
};
 
const char* ErrorMat::porque() const throw()
{
    switch (motivo)
    {
    case EDOMINIO: return "Error de Dominio ";break;
    case ERANGO:   return "Error de Rango ";break;
    }     
}
 
double logaritmo(const double n)
{
    try {
        if (n < 0) throw(ErrorMat(EDOMINIO) );
        if (n == 0) throw(ErrorMat(ERANGO) );
        return log(n);
        }
    catch(ErrorMat& e) {
        cout << e.porque();
    }
    return 0;
}
 
int main()
{
    double r = 100;
    cout << "log(" << r << ") = " << logaritmo(r) << endl;
    cout << "log(-" << r << ") = " << logaritmo(-r) << endl;
    cin.get();
    return 0;
}
Plantillas Arriba Librería Estándar de Plantillas
Herramientas personales