Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


1.6.4  Especificación de excepciones

§1  Sinopsis

En C++ existe una opción denominada especificación de excepción que permite señalar que tipo de excepciones puede lanzar una función directa o indirectamente (en funciones invocadas desde ella). Este especificador se utiliza en forma de sufijo en la declaración de la función [1] y tiene la siguiente sintaxis:

throw (<lista-de-tipos>)   // lista-de-tipos es opcional

La ausencia de especificador indica que la función puede lanzar cualquier excepción.

El mecanismo de excepciones fue introducido en el lenguaje en 1989, pero la primitiva versión adolecía de un problema que podemos resumir como sigue:  Supongamos que tenemos una función de librería cuya definición, contenida en un fichero de cabecera, es del tipo:

void somefuncion (int);

Lo normal es que las "tripas" de la función queden ocultas al usuario, que solo dispone de la información proporcionada por el prototipo ( 4.4.1), pero es evidente que en estas circunstancias es imposible saber si la función puede lanzar una excepción y en consecuencia, decidir si de deben tomar (o no) las medidas apropiadas para su captura.

Años después, y ante la confusión creada, el Comité de Estandarización decidió incluir la especificación de excepciones que comentamos en este capítulo. Como puede verse es un modo de incluir en el prototipo información suficiente para que el usuario conozca que tipo de excepciones pueden esperarse de una función (si es que las hay).


§2  Ejemplos de funciones con especificadores de excepción:

void f1();                 // f1 puede lanzar cualquier excepción
void f2() throw();         // f2 no puede lanzar excepciones

void f3() throw(BETA);     // f3 solo puede lanzar objetos BETA

void f4() throw(A, B*);    /* f4 puede lanzar excepciones derivadas públicamente de A o un puntero a derivada públicamente de B */

Nota:  La sintaxis utilizada con f2 es la forma estándar C++ para especificar que una función no puede lanzar excepciones, y que salvo indicación en contrario (§4 ), tampoco las funciones que puedan ser invocadas desde ella. No obstante, los compiladores Borland C++ y MS Visual C++ disponen de otra posibilidad sintáctica para el mismo propósito ( 4.4.1b).


§3    Tenga en cuenta que las funciones con especificador de excepción no son susceptibles de sustitución inline ( 4.4.6b). Por ejemplo, la sentencia:

inline void f1() throw(int) { ... }

daría lugar a una advertencia del compilador:   Warning: Functions with exception specifications are not expanded inline

§4    Las excepciones señaladas para una función no afectan a otras funciones que pudieran ser llamadas durante su ejecución. Por ejemplo:

func1() throw() {      // func1 no puede lanzar excepciones

  ...                  // en esta zona no se lanzarán excepciones

  func2();

}

func2() throw(A);      // func2 puede lanzar un objeto A

...

try {

 ...

 func1

}


Durante la ejecución del bloque de código de func1 no pueden lanzarse excepciones de ningún tipo, pero si ocurren circunstancias adecuadas mientras se está ejecutando la invocación a func2, desde esta sí pueden lanzarse objetos tipo A.

Todos los prototipos y definiciones de estas funciones deben tener un especificador de excepción conteniendo la misma <lista-de-tipos>.  Si una función lanza una excepción cuyo tipo no está incluido en su especificación, el programa llama a la función unexpected ( 1.6.3Excepciones imprevistas).


§5  El sufijo no es parte del tipo de la función; en consecuencia, un puntero a función no se verá afectado por el especificador de excepción que pueda tener la función. Este tipo de punteros solo comprueba el tipo de valor devuelto y los argumentos ( 4.2.4a). Por consiguiente, lo siguiente es legal:

void f2(void) throw();
void f3(void) throw(BETA);
void (* fptr)();           // Puntero a función devolviendo void
fptr = f2;                 // fptr se puede asignar a cualquiera

fptr = f3;                 // de las funciones f2 y f3


§6  Hay que prestar atención cuando se sobrecontrolan funciones virtuales, porque la especificación de excepción no se considera parte del tipo de función y existe el riesgo de violaciones en el diseño del programa.

§7  Ejemplo 1

En el siguiente ejemplo la definición de la clase derivada BETA::vfunc se hace de forma que no puede lanzar ninguna excepción; se trata de una variación de la definición original en la clase base.

class ALPHA {
   public:
   struct ALPHA_ERR {};
   virtual void vfunc(void) throw (ALPHA_ERR) {} // Especificador de excepción
};

class BETA : public ALPHA {
   void vfunc(void) throw() {} // Se cambia el especificador de excepción
};

§8  Ejemplo 3

Este ejemplo especifica que excepciones pueden lanzar las funciones festival y test.  Ninguna otra excepción podrá ser lanzadas desde ambas.

#include <stdio.h>
bool pass;
class Out{};
// festival solo puede lanzar excepciones Out

void festival(bool firsttime) throw(Out) {
  if(firsttime) throw Out();
}
void test() throw() {  // test no puede lanzar ninguna excepción
  try { festival(true); }
  catch(Out& e){ pass = true; }
}

int main() {
  pass = false;
  test();
  return pass ? (puts("Excepción manejada por test"),0) :
                (puts("Sin excepción!!") ,1);
}

Salida:

Excepción manejada por test

Si festival generase una excepción distinta de Out, se consideraría una excepción imprevista, y el control del programa sería transferido a la función prevista para estos casos (ver al respecto el ejemplo siguiente).

§9  Ejemplo 4

Se muestra que test no puede lanzar ninguna excepción. Si alguna función (por ejemplo el operador new) en el cuerpo de test lanza una excepción, la excepción debe ser capturada y manejada dentro del propio cuerpo de test. En caso contrario,  la excepción representaría una violación de la especificación de no-excepciones establecida para dicha función. Es posible establecer que set_unexpected() acepte un manejador diferente,  pero en cualquier caso, será invocada la función que se haya previsto para estos casos.

#include <except.h>
#include <process.h>
#include <stdio.h>
bool pass;
class Out{};
void imprevisto(){ puts("*** Fallo ***"); exit(1); }

void festival(bool firsttime) throw(Out) { // festival solo puede lanzar

  if(firsttime) throw Out();               // excepciones Out
}
void test() throw() {       // test no puede lanzar ninguna excepción
  try { festival(true); }
  catch(Out& e){ pass = true; throw; }   // Error: intenta ralanzar Out
}

int main() {                // ============
  set_unexpected(imprevisto);
  pass = false;
  test();
  return pass ? (puts("Excepción manejada por test"),0) :
                (puts("Sin excepción !!") ,1);
}

Salida:

*** Fallo ***

  Inicio.


[1]  Si en C++Builder coexisten simultanea e independientemente, un prototipo y una definición de la función, la especificación de excepción debe incluirse en ambos, de lo contrario se produciría un error de compilación.