catch - Oldemar Rodríguez Rojas

Anuncio
MANEJO DE
EXCEPCIONES
en C++
Dr. Oldemar Rodríguez R.
Escuela de Informática
Universidad Nacional

C++ posee un mecanismo de gestión de errores
incorporado que se denomina manejo de
excepciones. La utilización del manejo de
excepciones permite gestionar y responder a los
errores en tiempo de ejecución.

El manejo de excepciones de C++ está construido a
partir de tres palabras claves: try, catch y throw.

En términos generales, las sentencias de un
programa que se quiere monitorear, para vigilar las
excepciones, están contenidas en un bloque try. Si
se produce una excepción (un error) en un bloque
try, éste se eleva (utilizando throw) y usando catch
se captura la excepción y se procesa.

La forma general de try y catch es:
try {
// bloque try
throw excepción;
}
catch (type1 arg) {
// bloque catch
}
catch (type2 arg) {
// bloque catch
}
catch (type3 arg) {
// bloque catch
}
…………….
catch (typeN arg) {
// bloque catch
}

El bloque try debe contener la porción de programa que
se quiere monitorear. Esto puede ser tan corto como
unas pocas sentencias dentro de una función o tan
grande como el código de la función main() (que da
lugar a que se monitorear todo el programa).

Cuando se genera una excepción, la sentencia catch
correspondiente la captura y la procesa. Puede haber
más de una sentencia catch asociada a un bloque try.
La sentencia catch utilizada depende del tipo de
excepción. Esto es, si el tipo de datos especificado por
catch coincide con el de la excepción, se ejecuta esa
sentencia catch. (Las otras sentencias no se ejecutan).

Cuando se captura una excepción arg recibe un
valor. Puede capturar cualquier tipo de datos,
incluidas las clases creadas por el programador.

La forma general de la sentencia throw es la
siguiente:
throw exception;
throw debe ejecutarse dentro del bloque try,
que es lo más adecuado, o desde cualquier
función que la llame (directa o indirectamente),
exception es el valor generado.

Nota: Si se produce una excepción para la que
no existe una sentencia catch aplicable, el
programa puede terminar de forma anormal. Si
su compilador admite el estándar ANSI C++
propuesto, la generación de una excepción no
gestionada provoca una llamada a la función
terminate(). Por omisión, terminate() llama a
abort() para que su programa se detenga, pero,
si lo desea, puede especificar su propio gestor
de finalización.

EJEMPLOS
1.
Este sencillo ejemplo muestra la forma de funcionamiento de el manejo de
excepciones en C++:
// Un ejemplo sencillo de manejo de excepciones.
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
int main() {
cout << "inicio \n";
try { // inicio de un bloque try
cout << "Dentro del bloque try \n";
throw 10; // generación de un error
cout << "Esto no se ejecutará correctamente";
}
catch (int i) { // captura de un error
cout << "¡Capturado un error! Su número es: ";
cout << i << "\n";
}
cout << "fin";
return 0;
}

Este programa da lugar a la siguiente salida:
inicio
Dentro del bloque try
¡Capturado un error! Su número es: 10
fin

Observe este programa cuidadosamente. En él hay un bloque try
que contiene tres sentencias y una sentencia catch(int i) que
procesa una excepción entera. Dentro del bloque try, sólo se
ejecutarán dos de estas sentencias: la primera sentencia cout y la
sentencia throw. Una vez lanzada una excepción, el control pasa a
la expresión catch y el bloque try termina. Esto es, no se llama a
catch. En lugar de ello, se le transfiere la ejecución del programa.
(La pila se inicializa de nuevo para llevar esto a cabo.) De este
modo, la sentencia cout, que sigue throw, no se ejecutará.

Cuando se ejecuta la sentencia catch el programa continúa con las
sentencias que siguen a catch. Sin embargo, un bloque catch
finalizará normalmente con una llamada a exit(), abort(), etc.,
porque el manejo de excepciones se utiliza a menudo para
gestionar errores fatales.
2.
Como ya se ha indicado, el tipo de una excepción debe coincidir con el tipo
especificado en una sentencia catch. En el ejemplo anterior, si se cambia a double el
tipo de la sentencia catch, no podrá capturarse la excepción y el programa terminará
de un modo anormal. Este programa refleja este cambio:
// Este ejemplo no va a funcionar.
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
int main( ) {
cout << "inicio\n";
try { // inicio de un bloque try
cout << "Dentro del bloque try\n";
throw 10; // generación de un error
cout << "Esto no se ejecutará correctamente";
}
catch (double i) {
// No funcionará para una excepción entera
cout << "¡Capturado uno! Su número es: ";
cout << i << "\n";
}
cout << "fin";
return 0;
}
Este programa da lugar a la siguiente salida, ya que la sentencia double catch no capturará la excepción entera:
inicio
Dentro del bloque try
Detención anormal del programa
3.
Se puede generar una excepción desde una sentencia que no está dentro del
bloque try siempre que esté incluida en una función que esté, a su vez, dentro del
bloque try. Este programa, por ejemplo, es correcto:
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
void Xtest(int test) {
cout << "Dentro de Xtest, test vale: " << test << "\n";
if(test!=0) throw test;
}
int main() {
cout << "inicio\n";
try {
// inicio de un bloque try
cout << "Dentro del bloque try\n";
Xtest(0);
Xtest(1);
Xtest(2);
}
catch (int i) { //
captura de un error
cout << "Capturado uno! Su número es: ";
cout << i << "\n";
}
cout << "fin\n";
system("pause");
system("cls");
return 0;
}
• El programa anterior produce la salida:
inicio
Dentro del bloque try
Dentro de Xtest, test vale: 0
Dentro de Xtest, test vale: 1
¡Capturado uno! Su número es: 1
fin
4.
Un bloque try puede encontrarse dentro de una función. Cuando se da
este caso, cada vez que se introduce la función, se redefine el manejo de
excepciones con respecto a esa función.
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
void Xhandler(int test) {
try {
if(test!=0) throw test;
}
catch(int i) {
cout << "Capturado uno! Excep#: " << i << "\n";
}
}
int main() {
cout << "inicio\n";
Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout << "fin\n";
system("pause");
system("cls");
return 0;
}

El programa anterior produce la salida:
inicio
¡Capturado uno! Excep#: 1
¡Capturado uno! Excep#: 2
¡Capturado uno! Excep#: 3
fin
5. Como ya se comentó anteriormente,
puede haber más de una sentencia
catch asociada con un bloque try. De
hecho, lo normal es que sea así. Sin
embargo, cada sentencia catch debe
capturar un tipo diferente de excepción.
Por ejemplo, el siguiente programa captura
enteros y cadenas:
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
void Xhandler(int test) {
try {
if(test!=0)
throw test;
else
throw "El valor es cero";
}
catch(int i) {
cout << "Capturado uno! Ex.#: " << i << '\n';
}
catch(char *str) {
cout << "Capturada una cadena: ";
cout << str << '\n';
}
}
int main() {
cout << "inicio\n";
Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout << "fin\n";
system("pause");
system("cls");
return 0;
}
Este programa produce la siguiente
salida:
inicio
¡Capturado uno! ExA: 1
¡Capturado uno! ExA: 2
Capturada una cadena: El valor es cero
¡Capturado uno! Ex.#: 3 fin

Como se observa, cada sentencia catch responde sólo a su propio
tipo.

En general, las expresiones catch se comprueban de acuerdo a su
orden de aparición dentro del programa. Sólo se ejecuta la
sentencia que coincida. El resto de bloques catch se ignoran.



Existen diversos detalles y matices del manejo
de excepciones en C++ que lo hacen fácil de
utilizar.
En algunas circunstancias se necesitará un
gestor de excepciones que capture todas ellas
en lugar de capturar una de un determinado
tipo. Esto es sencillo de llevar a cabo.
Basta con utilizar simplemente esta forma de
catch:
catch(...) {
// procesamiento de todas las excepciones
}
El siguiente programa presenta catch(...)
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
void Xhandler(int test) {
try {
if(test==0) throw test; // genera un entero
if(test==1) throw 'a'; // genera un carácter
if(test==2) throw 123.23; // genera un doble
}
catch(...) { //
captura todas las execpciones
cout << "Capturada una!\n";
}
}
int main () {
cout << "inicio\n";
Xhandler(0);
Xhandler(1);
Xhandler(2);
cout << "fin\n";
system("pause");
system("cls");
return 0;
}

Este programa muestra la siguiente salida:
inicio
¡Capturada una!
¡Capturada una!
¡Capturada una!
fin

Los tres throw han sido capturados utilizando
una única sentencia catch.

Un buen uso para catch(...) es el de última
sentencia de una agrupación de capturas.

Por ejemplo, la siguiente versión,
ligeramente diferente del programa anterior,
captura explícitamente excepciones enteras
dejando que catch(...) capture todas las
demás:
#include <iostream>
using std::endl;
using std::cin;
using std::cout;
void Xhandler(int test) {
try {
if(test==0) throw test; // genera un entero
if(test==1) throw 'a'; // genera un carácter
if(test==2) throw 123.23; // genera un doble
}
catch(int i) { //
captura todas las execpciones
cout << "Capturada " << i << '\n';
}
catch(...) { //
captura todas las execpciones
cout << "Capturada una!\n";
}
}
int main () {
cout << "inicio\n";
Xhandler(0);
Xhandler(1);
Xhandler(2);
cout << "fin\n";
system("pause");
system("cls");
return 0;
}

La salida producida por este programa es:
inicio
Capturada 0
¡Capturada una!
¡Capturada una!
fin


Como sugiere el ejemplo, el uso de catch(...) por
omisión es una buena forma de capturar todas las
excepciones que no desee gestionar explícitamente.
La captura de todas las excepciones impide que una
excepción incontrolada provoque una terminación
anormal de un programa.
Otros ejemplos:
División entre 0  ver Ej8
 Excepción en el operador New  ver Ej9

Descargar