Especificaciones de excepciones

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.

Excepciones en constructores y destructores

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, 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;
   }
}  

Excepciones estándar

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.

 
Relanzar una excepción

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.

 | Ir al Principio 5a2h2o

Compartir

Mi nombre es Alexander fundador y CEO, y me gusta llamarme un Geek. Amo la informática, tecnología y todo lo que está relacionado con ella. Inicié este sitio con la intención de compartir conocimientos como cursos en línea, tutoriales y videotutoriales. Estoy muy entusiasmado con la información que he descubierto y compartido hasta el momento. La verdad es que lo he hecho con el mayor de los gustos. Así es, soy un Geek con una visión para compartir conocimiento. Leer mas...