He visto al menos una fuente confiable (una clase de C ++ que tomé) que recomienda que las clases de excepción específicas de la aplicación en C ++ deberían heredar std::exception
. No tengo claros los beneficios de este enfoque.
En C #, las razones para heredar de ApplicationException
son claras: obtienes un puñado de métodos, propiedades y constructores útiles y solo tienes que agregar o anular lo que necesitas. Con std::exception
parece que lo único que consigue es un what()
método para anular, que sólo podría así crear tú mismo.
Entonces, ¿cuáles son los beneficios, si los hay, de usar std::exception
como clase base para mi clase de excepción específica de la aplicación? ¿Existen buenas razones para no heredar std::exception
?
c++
exception
exception-handling
John M. Gant
fuente
fuente
Respuestas:
El principal beneficio es que el código que usa sus clases no tiene que saber el tipo exacto de lo que está
throw
haciendo, sino que puede simplementecatch
elstd::exception
.Editar: como señalaron Martin y otros, en realidad desea derivar de una de las subclases de
std::exception
declarado en<stdexcept>
header.fuente
std::exception
se define en el<exception>
encabezado ( prueba ).-1
El problema
std::exception
es que no hay ningún constructor (en las versiones compatibles con el estándar) que acepte un mensaje.Como resultado, prefiero derivar de
std::runtime_error
. Esto se deriva de,std::exception
pero sus constructores le permiten pasar una C-String ostd::string
a al constructor que se devolverá (como achar const*
) cuandowhat()
se llame.fuente
La razón para heredar
std::exception
es la clase base "estándar" para las excepciones, por lo que es natural que otras personas en un equipo, por ejemplo, esperen eso y atrapen la basestd::exception
.Si busca comodidad, puede heredar de lo
std::runtime_error
que proporcionastd::string
constructor.fuente
std::exception
es una excelente idea. Lo que no debes hacer es heredar de más de uno.Una vez participé en la limpieza de una gran base de código donde los autores anteriores habían arrojado ints, HRESULTS, std :: string, char *, clases aleatorias ... cosas diferentes en todas partes; solo nombre un tipo y probablemente fue arrojado a alguna parte. Y ninguna clase base común en absoluto. Créame, las cosas estaban mucho más ordenadas una vez que llegamos al punto en que todos los tipos lanzados tenían una base común que podíamos atrapar y sabíamos que nada iba a pasar. Así que hágase un favor a usted mismo (ya aquellos que tendrán que mantener su código en el futuro) y hágalo de esa manera desde el principio.
fuente
Debería heredar de boost :: exception . Proporciona muchas más funciones y formas bien entendidas de transportar datos adicionales ... por supuesto, si no está usando Boost , ignore esta sugerencia.
fuente
Sí, deberías derivar
std::exception
.Otros han respondido que
std::exception
tiene el problema de que no puede pasarle un mensaje de texto, sin embargo, generalmente no es una buena idea intentar formatear un mensaje de usuario en el punto del lanzamiento. En su lugar, utilice el objeto de excepción para transportar toda la información relevante al sitio de captura, que luego puede formatear un mensaje fácil de usar.fuente
std::exception
y llevar la información de la excepción. Pero, ¿quién será responsable de crear / formatear el mensaje de error? ¿La::what()
función o algo más?La razón por la que puede querer heredar
std::exception
es porque le permite lanzar una excepción que se detecta según esa clase, es decir:fuente
Hay un problema con la herencia que debe conocer es la división de objetos. Cuando escribe
throw e;
una expresión de lanzamiento inicializa un objeto temporal, llamado objeto de excepción, el tipo del cual se determina eliminando cualquier calificador cv de nivel superior del tipo estático del operando dethrow
. Eso podría no ser lo que esperabas. Ejemplo de problema que puede encontrar aquí .No es un argumento en contra de la herencia, es solo información "imprescindible".
fuente
throw;
está bien, pero no es obvio que debas escribir algo como esto.raise()
como tal:virtual void raise() { throw *this; }
. Solo recuerde anularlo en cada excepción derivada.Diferencia: std :: runtime_error vs std :: exception ()
Si debe heredar de él o no, depende de usted. Standard
std::exception
y sus descendientes estándar proponen una posible estructura de jerarquía de excepción (división enlogic_error
subjerarquía yruntime_error
subjerarquía) y una posible interfaz de objeto de excepción. Si te gusta, úsalo. Si por alguna razón necesita algo diferente, defina su propio marco de excepción.fuente
Dado que el lenguaje ya lanza std :: exception, debe detectarlo de todos modos para proporcionar un informe de error decente. También puede usar esa misma captura para todas las excepciones inesperadas propias. Además, casi cualquier biblioteca que arroje excepciones las derivaría de std :: exception.
En otras palabras, es
o
Y la segunda opción es definitivamente mejor.
fuente
Si todas sus posibles excepciones se derivan de
std::exception
, su bloque de captura puede simplementecatch(std::exception & e)
y estar seguro de capturar todo.Una vez que haya capturado la excepción, puede usar ese
what
método para obtener más información. C ++ no admite la escritura de pato, por lo que otra clase con unwhat
método requeriría una captura diferente y un código diferente para usarlo.fuente
Si derivar de cualquier tipo de excepción estándar o no es la primera pregunta. Al hacerlo, se habilita un único controlador de excepciones para todas las excepciones estándar de la biblioteca y la suya propia, pero también fomenta esos controladores "catch-them-all". El problema es que solo se deben detectar las excepciones que se sabe cómo manejar. En main (), por ejemplo, detectar todas las excepciones std :: es probablemente algo bueno si la cadena what () se registrará como último recurso antes de salir. En otros lugares, sin embargo, es poco probable que sea una buena idea.
Una vez que haya decidido si derivar de un tipo de excepción estándar o no, la pregunta es cuál debería ser la base. Si su aplicación no necesita i18n, podría pensar que formatear un mensaje en el sitio de la llamada es tan bueno como guardar información y generar el mensaje en el sitio de la llamada. El problema es que es posible que el mensaje formateado no sea necesario. Es mejor usar un esquema de generación de mensajes perezoso, quizás con memoria preasignada. Luego, si el mensaje es necesario, se generará al acceder (y, posiblemente, se almacenará en caché en el objeto de excepción). Por lo tanto, si el mensaje se genera cuando se lanza, entonces se necesita un derivado std :: exception, como std :: runtime_error como clase base. Si el mensaje se genera de forma perezosa, entonces std :: exception es la base apropiada.
fuente
Otra razón para las excepciones de subclases es un mejor aspecto de diseño cuando se trabaja en grandes sistemas encapsulados. Puede reutilizarlo para cosas como mensajes de validación, consultas de usuarios, errores fatales del controlador, etc. En lugar de reescribir o reenganchar todos sus mensajes de validación, simplemente puede "capturarlos" en el archivo fuente principal, pero arrojar el error en cualquier parte de su conjunto completo de clases.
Por ejemplo, una excepción fatal terminará el programa, un error de validación solo borrará la pila y una consulta de usuario hará una pregunta al usuario final.
Hacerlo de esta manera también significa que puede reutilizar las mismas clases pero en diferentes interfaces. Por ejemplo, una aplicación de Windows puede usar un cuadro de mensaje, un servicio web mostrará html y el sistema de informes lo registrará y así sucesivamente.
fuente
Aunque esta pregunta es bastante antigua y ya se ha respondido mucho, solo quiero agregar una nota sobre cómo hacer un manejo adecuado de excepciones en C ++ 11, ya que continuamente me falta esto en las discusiones sobre excepciones:
Utilice
std::nested_exception
ystd::throw_with_nested
Se describe en StackOverflow aquí y aquí , cómo puede obtener un seguimiento de sus excepciones dentro de su código sin necesidad de un depurador o un registro engorroso, simplemente escribiendo un controlador de excepciones adecuado que volverá a generar excepciones anidadas.
Dado que puede hacer esto con cualquier clase de excepción derivada, ¡puede agregar mucha información a dicho seguimiento! También puede echar un vistazo a mi MWE en GitHub , donde un backtrace se vería así:
Ni siquiera necesita crear una subclase
std::runtime_error
para obtener mucha información cuando se lanza una excepción.El único beneficio que veo en la subclasificación (en lugar de solo usar
std::runtime_error
) es que su controlador de excepciones puede detectar su excepción personalizada y hacer algo especial. Por ejemplo:fuente