Se puede añadir una especificación de las posibles excepciones que puede producir una función:
<tipo> <identificador>(<parametros>) throw(<lista_excepciones>);
De este modo indicamos que la función sólo puede hacer un "throw" de uno de los tipos especificados en la lista, si la lista está vacía indica que la función no puede producir excepciones.
El compilador no verifica si realmente es así, es decir, podemos hacer un "throw" con un objeto de uno de los tipos listados, y el compilador no notificará ningún error. Sólo se verifica durante la ejecución, de modo que si se produce una excepción no permitida, el programa sencillamente termina.
Veamos algunos ejemplos:
int Compara(int, int) throw();
Indica que la función "Compara" no puede producir excepciones.
int CrearArray(int) throw(std::bad_alloc);
Indica que la función "CrearArray" sólo puede producir excepciones por memoria insuficiente.
int MiFuncion(char *) throw(std::bad_alloc, ExDivCero);
Indica que la función "MiFuncion" puede producir excepciones por falta de memoria o por división por cero.
Uno de los lugares donde más frecuentemente se requiere un tratamiento de excepciones es en los constructores de las clases, normalmente, esos constructores hacen peticiones de memoria, verifican condiciones, leen valores iniciales desde ficheros, etc.
Aunque no hay ningún problema en eso, no es así con los destructores, en está desaconsejado que los destructores puedan producir excepciones. La razón es sencilla, los destructores pueden ser invocados automáticamente cuando se procesa una excepción, y si durante ese proceso se produce de nuevo una excepción, el programa terminará inmediatamente.
Sin embargo, si necesitamos generar una excepción desde un destructor, existe un mecanismo que nos permite comprobar si se está procesando una excepción, y en ese caso, no ejecutamos la sentencia "throw". Se trata de la función estándar: "uncaught_exception", que devuelve el valor "true" si se está procesando una excepción":
class CopiaEx {}; Miclase::~MiClase() throw (CopiaEx) { // Necesitamos copiar un fichero cuando se // destruya un objeto de esta clase, pero // CopiaFichero puede generar una excepción // De modo que antes averiguamos si ya se // está procesando una: if(uncaught_exception()) return; // No hacemos nada // En caso contrario, intentamos hacer la copia: CopiaFichero("actual.log", "viejo.log"); }
Pero, es mejor idea hacer el tratamiento de excepciones dentro del propio destructor:
class CopiaEx {}; Miclase::~MiClase() throw () { try { CopiaFichero("actual.log", "viejo.log"); } catch(CopiaEx&) { cout << "No se pudo copiar el fichero 'actual.log'" << endl; } }
Existen cuatro excepciones estándar, derivadas de la clase "exception", y asociadas a un operador o a un error de especificación:
std::bad_alloc // Al operador new std::bad_cast // Al operador dynamic_cast<> std::bad_typeid // Al operador typeid std::bad_exception // Cuando se viola una especificación
Cada vez que se usa uno de los operadores mencionados, puede producirse una excepción. Un programa bien hecho debe tener esto en cuenta, y hacer el tratamiento de excepciones cuando se usen esos operadores. Esto creará aplicaciones robustas y seguras.
Ya sabemos que los bloques "try" pueden estar anidados, y que si se produce una excepción en un nivel interior, y no se captura en ese nivel, se lanzará la excepción al siguiente nivel en el orden de anidamiento.
Pero también podemos lanzar una excepción a través de los siguientes niveles, aunque la hayamos capturado. A eso se le llama relanzarla, y para ello se usa "throw;", sin argumentos:
#include <iostream> using namespace std; void Programa(); int main() { try { // Programa Programa(); } catch(int x) { cout << "Excepción relanzada capturada." << endl; cout << "error: " << x << endl; } catch(...) { cout << "Excepción inesperada." << endl; } cin.get(); return 0; } void Programa() { try { // Operaciones... throw 10; } catch(int x) { // Relanzar, no nos interesa manejar aquí throw; } }
La función no hace nada con la excepción capturada, excepto reenviarla a nivel siguiente, donde podremos capturarla de nuevo.
© Agosto de 2003 Salvador Pozo, salvador@conclase.net