Bloque try
Asocia uno o más controladores de excepciones (cláusulas catch) con una instrucción compuesta.
Sintaxis
try instrucción-compuesta sec-de-controladores
|
|||||||||
donde la sec-de-controladores es una secuencia de uno o más controladores que tiene la sintaxis siguiente:
catch ( atrib (opcional) sec-especificadores-de-tipo declarador ) instrucción-compuesta
|
(1) | ||||||||
catch ( atrib (opcional) sec-especificadores-de-tipo declarador-abstracto (opcional) ) instrucción-compuesta
|
(2) | ||||||||
catch ( ... ) instrucción-compuesta
|
(3) | ||||||||
| instrucción-compuesta | - | secuencia de instrucciones encerrada entre llaves |
| atrib(desde C++11) | - | lista opcional de atributos, se aplica al parámetro formal |
| sec-especificadores-de-tipo | - | parte de una declaración de parámetro formal, lo mismo que en una lista de parámetros de función |
| declarador | - | parte de una declaración de parámetro formal, lo mismo que en una lista de parámetros de función |
| declarador-abstracto | - | parte de un declaración de parámetro formal sin denominación, lo mismo que en una lista de parámetros de función |
catch que declara un parámetro formal denominado
try { /* */ } catch (const std::exception& e) { /* */ }
catch que declara un parámetro sin denominación
try { /* */ } catch (const std::exception&) { /* */ }
catch general, que se activa para cualquier excepción
try { /* */ } catch (...) { /* */ }
Explicación
- Véase lanzamiento de excepciones para mayor información sobre expresiones de lanzamiento.
Un bloque try es una instrucción, y como tal, puede aparecer en cualquier lugar donde pueda aparecer una instrucción (es decir, como una de las intrucciones en una instrucción compuesta, incluida la instrucción compuesta del cuerpo de la función). Véase bloque try de función para los bloques try alrededor de los cuerpos de funciones. La siguiente descripción se aplica tanto a los bloques try como a los bloques try de función.
El parámetro formal de la cláusula catch (sec-especificadores-de-tipo y declarador o sec-especificadores-de-tipo y declarador-abstracto) determina qué tipos de excepciones hacen que se ingrese a esta cláusula catch. No puede ser un tipo de referencia rvalue, (desde C++11) un tipo clase abstracta, un tipo incompleto, o puntero a tipo incompleto (excepto que punteros a void (que pueden estar calificados-cv) están permitidos). Si el tipo del parámetro formal es tipo de array o tipo de función, se trata como el tipo puntero correspondiente (similar a una declaración de función).
Cuando se lanza una excepción por cualquier instrucción en instrucción-compuesta, el objeto excepción de tipo E se compara con los tipos de los parámetros formales T de cada cláusula catch en sec-de-controladores, en el orden en que se enumeran las cláusulas catch. La excepción es una coincidencia si se cumple alguna de las siguientes condiciones:
EyTson del mismo tipo (ignorando los calificadores-cv de nivel superior enT);Tes una referencia lvalue (que puede estar calificada-cv) aE;Tes una clase base pública inequívoca deE;Tes una referencia a una clase base pública inequívoca deE;Tes (puede estar calificado-cv)Uoconst U &(desde C++14), yUes un puntero o puntero a tipo miembro, yEtambién es un tipo de puntero o puntero a tipo miembro que es implícitamente convertible aUpor uno o más de:
- * una conversión de puntero estándar que no sea a una clase base privada, protegida o ambigua;
- * una conversión de calificación;
| (desde C++17) |
Tes un puntero o un puntero a miembro o una referencia a un puntero constante (desde C++14), mientras queEes std::nullptr_t.
try
{
f();
}
catch (const std::overflow_error& e)
{
// esto se ejecuta si f() lanza std::overflow_error (regla de mismo tipo)
}
catch (const std::runtime_error& e)
{
// esto se ejecuta si f() lanza std::underflow_error (regla de clase base)
} catch (const std::exception& e)
{
// esto se ejecuta si f() lanza std::logic_error (regla de clase base)
}
catch (...)
{
// esto se ejecuta si f() lanza std::string o int o cualquier otro tipo no relacionado
}
La cláusula general catch (...) coincide con las excepciones de cualquier tipo. Si está presente, debe ser la última cláusula catch en la sec-de-controladores. El bloque catch general se puede usar para garantizar que ninguna excepción no capturada pueda escapar de una función que ofrece la garantía de excepción de no lanzamiento.
Si no se encuentran coincidencias después de examinar todas las cláusulas catch, la propagación de excepción continúa hasta el bloque try que lo contiene, como se describe en la expresión de lanzamiento. Si no quedan bloques try que los contenga, se ejecuta std::terminate (en este caso, se define por la implementación si se produce algún desenredo de la pila: se permite lanzar una excepción no atrapada para terminar el programa sin invocar ningún destructor).
Al ingresar a una cláusula catch, si su parámetro formal es una clase base del tipo de excepción, es inicializada mediante la inicialización de copia desde el subobjeto de la clase base del objeto de excepción. De lo contrario, se inicializa por medio de la inicialización de copia desde el objeto de excepción (esta copia está sujeta a la elisión de copia).
try
{
std::string("abc").substr(10); // lanza std::length_error
}
// catch (std::exception e) // inicialización de copia desde la base std::exception
// {
// std::cout << e.what(); // información sobre length_error se perdió
// }
catch (const std::exception& e) // referencia a base de un objeto polimorfo
{
std::cout << e.what(); // información de length_error se imprime
}
Si el parámetro de la cláusula catch es un tipo referencia, cualquier cambio realizado en él se refleja en el objeto de excepción, y puede observarse por otro controlador si la excepción se vuelve a lanzar con throw;. Si el parámetro no es una referencia, cualquier cambio realizado en él es local y su duración termina cuando el controlador termina.
Dentro de una cláusula catch, std::current_exception puede usarse para capturar la excepción en un std::exception_ptr, y std::throw_with_nested puede ser usada para construir excepciones anidadas. |
(desde C++11) |
No se usará un instrucción goto o switch para transferir el control a un bloque try o a un controlador.
Aparte de lanzar o volver a lanzar la excepción, la cláusula catch después de un bloque try normal (no un bloque try de función) puede salir con una instrucción return, continue, break, goto, o llegar al final de su instrucción-compuesta. En cualquier caso, esto destruye el objeto de excepción (a menos que exista una instancia de std::exception_ptr que se refiera a él).
Notas
No se garantiza que la expresión de lanzamiento throw NULL; coincida con una cláusula catch de puntero, porque el tipo de objeto de excepción puede ser int, pero throw nullptr; seguramente se corresponde con cualquier cláusula catch de puntero o de puntero a miembro.
Si se coloca una cláusula catch para una clase derivada después de la cláusula catch para una clase base, la cláusula catch de la derivada nunca se ejecutará.
try
{
f();
} catch (const std::exception& e)
{
// se ejecutará si f() lanza std::runtime_error
}
catch (const std::runtime_error& e)
{
// ¡código muerto!
}
Si se utiliza goto para salir de un bloque try y si alguno de los destructores de variables automáticas con ámbito de bloque que se ejecutan por la instrucción goto arroja excepciones, esas excepciones se atrapan por los bloques try en los que se definen las variables:
label:
try
{
T1 t1;
try
{
T2 t2;
if(condition)
goto label; // destruye a t2, luego destruye a t1, luego salta a la etiqueta
}
catch (...) { } // atrapa la excepción del destructor de t2
}
catch (...) { } // atrapa la excepción del destructor de t1
Palabras clave
Ejemplo
El siguiente ejemplo demuestra varios usos del bloque try
#include <iostream>
#include <vector>
int main()
{
try
{
std::cout << "Lanzando una excepción de un entero...\n";
throw 42;
}
catch (int i)
{
std::cout << " la excepción del entero se atrapo, con valor: " << i << '\n';
}
try
{
std::cout << "Creando un vector con 5 elementos... \n";
std::vector<int> v(5);
std::cout << "Accediendo al onceavo elemento del vector...\n";
std::cout << v.at(10); // vector::at() lanza std::out_of_range
}
catch (const std::exception& e) // atrapado por referencia a la base
{
std::cout << " se atrapo una excepción estándar, con mensaje '"
<< e.what() << "'\n";
}
}
Posible salida:
Lanzando una excepción de un entero...
la excepción del entero se atrapo, con valor: 42
Creando un vector con 5 elementos...
Accediendo al onceavo elemento del vector...
se atrapo una excepción estándar, con mensaje 'out_of_range'
Defect reports
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
| ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 98 | C++98 | una instrucción switch podía transferir el control a un bloque try o aun controlador |
prohibido |
| CWG 210 | C++98 | la expresión throw se comparaba con las cláusulas catch | el objecto excepción se compara con las cláusulas catch |
| CWG 1166 | C++98 | no se especificó el comportamiento cuando coincide una cláusula catch cuyo tipo de excepción en una referencia a un tipo de clase abstracta |
no se permiten los tipos de clase abstracta para las cláusulas catch |
| CWG 1769 | C++98 | cuando el tipo de excepción declarado en la cláusula catch es una base del tipo del objeto de excepción, se podría usar un constructor de conversión para la inicialización del parámetro de la cláusula catch |
el parámetro se inicializa mediante copia desde el subobjeto de clase base correspondiente del objeto de excepción |
| CWG 2093 | C++98 | un objeto excepción de puntero a tipo objeto no podía coincidir con un controlador de puntero a tipo objeto mediante la conversión de calficación |
permitido |