Al diseñar mi primera biblioteca C ++ 'seria', me pregunto:
¿Es un buen estilo derivar las excepciones std::exception
y sus descendientes?
Incluso después de leer
- Diseñando clases de excepción
- ¿Cuál es un 'buen número' de excepciones para implementar en mi biblioteca?
Todavía no estoy seguro. Porque, además de la práctica común (pero quizás no buena), supondría, como usuario de la biblioteca, que una función de biblioteca arrojaría std::exception
s solo cuando las funciones de biblioteca estándar fallaran en la implementación de la biblioteca, y no puede hacer nada al respecto. Pero aún así, al escribir el código de la aplicación, para mí es muy conveniente, y también en mi humilde opinión apuesto a tirar un std::runtime_error
. Además, mis usuarios también pueden confiar en la interfaz mínima definida, como what()
o códigos.
Y, por ejemplo, mi usuario proporciona argumentos defectuosos, ¿qué sería más conveniente que lanzar un std::invalid_argument
, no? Entonces, combinado con el uso común de std :: excepción que veo en el código de otros: ¿Por qué no ir más allá y derivar de su clase de excepción personalizada (por ejemplo, lib_foo_exception) y también de std::exception
.
Pensamientos?
fuente
std::exception
no significa que arrojes unstd::exception
. Además,std::runtime_error
hereda destd::exception
en primer lugar, y elwhat()
método proviene destd::exception
, nostd::runtime_error
. Y definitivamente debe crear sus propias clases de excepción en lugar de lanzar excepciones genéricas comostd::runtime_error
.lib_foo_exception
clase se derivastd::exception
, el usuario de la biblioteca capturaríalib_foo_exception
simplemente capturandostd::exception
, además de cuando atrapa solo el de biblioteca. Por lo tanto, también podría preguntar si mi clase de raíz de excepción de biblioteca hereda de std :: exception .lib_foo_exception
?" Con heredar destd::exception
usted puede hacerlo porcatch(std::exception)
OR porcatch(lib_foo_exception)
. Sin derivar destd::exception
, lo atraparías si y solo si , porcatch(lib_foo_exception)
.catch(...)
. Está allí porque el lenguaje permite el caso que está considerando (y las bibliotecas que "se comportan mal"), pero esa no es la mejor práctica moderna.catch
sitios más generales y más generales , y también transacciones más generales que modelan una operación de usuario final. Si lo compara con idiomas que no promueven la idea de la captura generalizada destd::exception&
, por ejemplo, a menudo tienen mucho más código contry/catch
bloques intermedios relacionados con errores muy específicos, lo que disminuye un poco la generalidad del manejo de excepciones a medida que comienza a ubicarse un énfasis mucho más fuerte en el manejo manual de errores, y también en todos los errores dispares que podrían ocurrir.Respuestas:
Todas las excepciones deben heredar de
std::exception
.Supongamos, por ejemplo, que necesito llamar
ComplexOperationThatCouldFailABunchOfWays()
y quiero manejar cualquier excepción que pueda arrojar. Si todo hereda destd::exception
, esto es fácil. Solo necesito un solocatch
bloque, y tengo una interfaz estándar (what()
) para obtener detalles.Si las excepciones NO heredan
std::exception
, esto se vuelve mucho más feo:Con respecto a lanzar
runtime_error
o noinvalid_argument
crear sus propiasstd::exception
subclases para lanzar: Mi regla general es introducir una nueva subclase cada vez que necesito manejar un tipo particular de error de manera diferente a otros errores (es decir, cada vez que necesito uncatch
bloque separado ).runtime_error
tirado aquí significa algo diferente a un error de ejecución genérico), entonces corro el riesgo de entrar en conflicto con otros usos de la subclase existente.invalid_argument
), entonces reutilizo la clase existente. Simplemente no veo mucho beneficio al agregar una nueva clase en este caso. (Las Pautas principales de C ++ no están de acuerdo conmigo aquí; recomiendan usar siempre sus propias clases).Las Pautas principales de C ++ tienen más discusión y ejemplos.
fuente
catch (...)
(con los puntos suspensivos literales) es?catch (...)
solo es útil si no necesita hacer nada con lo que se arroja. Si desea hacer algo, por ejemplo, mostrar o registrar el mensaje de error específico, como en mi ejemplo, entonces necesita saber qué es.Esa es una suposición incorrecta.
Los tipos de excepción estándar se proporcionan para uso "más común". No están diseñados para ser utilizados únicamente por la biblioteca estándar.
Sí, haga que todo finalmente herede
std::exception
. A menudo, eso implicará heredar destd::runtime_error
ostd::logic_error
. Lo que sea apropiado para la clase de excepción que está implementando.Por supuesto, esto es subjetivo: varias bibliotecas populares ignoran por completo los tipos de excepción estándar, presumiblemente para desacoplar las bibliotecas de la biblioteca estándar. ¡Personalmente creo que esto es extremadamente egoísta! Hace que la captura de excepciones sea mucho más difícil de acertar.
Hablando personalmente, a menudo solo tiro un
std::runtime_error
y termino. Pero eso es entrar en una discusión sobre qué tan granular hacer sus clases de excepción, que no es lo que está preguntando.fuente