Curso de C++ v2.0
Consultas, lista de correo 'C++ Con Clase' 'C++ Con Clase' página de entrada Librerías estándar C Tabla de contenido Contactar con Webmaster
*Introducción
*1 Toma de contacto
*2 Variables I
*3 Funciones I: Declaración y definición
*4 Operadores I
*5 Sentencias
*6 Declaración de variables
*7 Normas para la notación
*8 Cadenas de caracteres
*9 Conversión de tipos
*10 Variables II: Arrays
*11 Variables III: Estructuras
*12 Variables IV: Punteros 1
*13 Operadores II: Más operadores
*14 Operadores III: Precedencia
*15 Funciones II: Parámetros por valor y referencia
*16 Variables V: Uniones
*17 Variables VI: Punteros 2
*18 Operadores IV: De bits y condicional
*19 Definición de tipos
*20 Funciones III
*21 Funciones IV: Sobrecarga
*22 Operadores V: Sobrecarga
*23 El preprocesador
*24 Funciones V: Recursividad
*25 Variables VII: Modificadores
*26 Espacios con nombre
*27 Clases I: Definiciones
*28 Declaración de clases
*29 Constructores
*30 Destructores
*31 El puntero this
*32 Sistema de protección
*33 Modificadores para miembros
*34 Más sobre funciones
*35 Operadores sobrecargados
*36 Herencia
*37 Funciones virtuales
*38 Derivación múltiple
*39 Trabajar con ficheros
*40 Plantillas
*41 Punteros a miembros
*42 Castings
*43 Excepciones
 . Clase exception
 . Orden de captura
 . Especificaciones
 . Destructores
 . Estándar
 . Relanzar excepción
*Ejemplos capítulos 1 a 6
*Ejemplos capítulos 8 y 9
*A Palabras reservadas C/C++
*B Trigrafos y símbolos alternativos
*C Librerías estándar
*D Streams
<< < > >>

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

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.

<< < > >>