Exceptions
El manejo de excepciones proporciona una forma de transferir control e información desde algún punto en la ejecución de un programa a un manejador asociado con un punto previamente pasado por la ejecución (en otras palabras, el manejo de excepciones transfiere control hacia arriba en la pila de llamadas).
Evaluar una throw expresión lanzará una excepción. Las excepciones también pueden ser lanzadas en otros contextos .
Para que una excepción sea capturada, la expresión throw debe estar dentro de un try block , y el try block debe contener un handler que coincida con el tipo del objeto de excepción.
Al declarar una función, se puede proporcionar la(s) siguiente(s) especificación(es) para limitar los tipos de excepciones que una función puede lanzar:
| (hasta C++17) |
| (desde C++11) |
Los errores que surgen durante el manejo de excepciones son manejados por std::terminate y std::unexpected (hasta C++17) .
Contenidos |
Uso
Mientras que la expresión throw puede utilizarse para transferir control a cualquier bloque de código en la pila de ejecución, por razones arbitrarias (similar a std::longjmp ), su uso previsto es el manejo de errores.
Manejo de errores
Lanzar una excepción se utiliza para señalar errores desde funciones, donde "errores" típicamente se limitan únicamente a lo siguiente [1] [2] [3] :
- Fallos en cumplir las postcondiciones, como no producir un objeto de retorno válido.
- Fallos en cumplir las precondiciones de otra función que debe ser llamada.
- (para funciones miembro no privadas) Fallos en (re)establecer un invariante de clase.
En particular, esto implica que los fallos de los constructores (ver también RAII ) y la mayoría de los operadores deben reportarse lanzando excepciones.
Además, las llamadas funciones de contrato amplio utilizan excepciones para indicar entradas inaceptables, por ejemplo, std::basic_string::at no tiene precondiciones, pero lanza una excepción para indicar índice fuera de rango.
Seguridad de excepciones
Después de que una función reporta una condición de error, pueden proporcionarse garantías adicionales con respecto al estado del programa. Generalmente se reconocen los siguientes cuatro niveles de garantía de excepción [4] [5] [6] , los cuales son superconjuntos estrictos entre sí:
-
Garantía de excepción Nothrow (o nofail)
— la función nunca lanza excepciones. Se espera Nothrow (los errores se reportan por otros medios o se ocultan) de los
destructores
y otras funciones que pueden ser llamadas durante el desenrollado de pila.
Los
destructores
son
noexceptpor defecto. (desde C++11) Se espera Nofail (la función siempre tiene éxito) de los swaps, constructores de movimiento , y otras funciones utilizadas por aquellas que proporcionan garantía fuerte de excepción. - Garantía fuerte de excepción — Si la función lanza una excepción, el estado del programa se revierte al estado justo antes de la llamada a la función (por ejemplo, std::vector::push_back ).
- Garantía básica de excepción — Si la función lanza una excepción, el programa se encuentra en un estado válido. No hay fugas de recursos y todas las invariantes de los objetos permanecen intactas.
- Sin garantía de excepción — Si la función lanza una excepción, el programa puede no estar en un estado válido: pueden haber ocurrido fugas de recursos, corrupción de memoria u otros errores que destruyan invariantes.
Los componentes genéricos pueden, además, ofrecer
garantía de neutralidad ante excepciones
: si se lanza una excepción desde un parámetro de plantilla (por ejemplo, desde el
Compare
objeto función de
std::sort
o desde el constructor de
T
en
std::make_shared
), se propaga, sin cambios, al llamador.
Objetos de excepción
Mientras que objetos de cualquier tipo completo y punteros cv a void pueden ser lanzados como objetos de excepción, todas las funciones de la biblioteca estándar lanzan objetos anónimos por valor, y los tipos de esos objetos se derivan (directa o indirectamente) de std::exception . Las excepciones definidas por el usuario normalmente siguen este patrón. [7] [8] [9]
Para evitar la copia innecesaria del objeto de excepción y el object slicing, la mejor práctica para los manejadores es capturar por referencia. [10] [11] [12] [13]
Notas
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_constexpr_exceptions
|
202411L
|
(C++26) | constexpr excepciones |
Enlaces externos
|