¿Cuándo y cómo debo utilizar el manejo de excepciones?

82

Estoy leyendo sobre el manejo de excepciones. Obtuve información sobre qué es el manejo de excepciones, pero tengo algunas preguntas:

  1. ¿Cuándo lanzar una excepción?
  2. En lugar de lanzar una excepción, ¿podemos usar un valor de retorno para indicar el error?
  3. Si protejo todas mis funciones mediante bloques try-catch, ¿no reducirá el rendimiento?
  4. ¿Cuándo usar el manejo de excepciones?
  5. Vi un proyecto donde todas y cada una de las funciones de ese proyecto contenían un bloque try-catch (es decir, el código dentro de toda la función está rodeado por un bloque try-catch). ¿Es esta una buena practica?
  6. ¿Cuál es la diferencia entre try-catch y __try __except?
Umesha MS
fuente
Deberá hacer preguntas más específicas que "Cuándo lanzar una excepción". Lanzas una excepción cuando sucede algo excepcional.
Falmarri
Escribí sobre esto en relación con PHP, pero creo que casi todo es aplicable a C ++. Consulte la publicación del blog .
ircmaxell

Respuestas:

95

Aquí hay una guía bastante completa sobre excepciones que creo que es una lectura obligada:

Excepciones y manejo de errores - Preguntas frecuentes de C ++ o Preguntas frecuentes de C ++ lite

Como regla general, una excepción cuando el programa puede identificar una externaproblema que impide la ejecución. Si recibe datos del servidor y esos datos no son válidos, inicie una excepción. ¿Sin espacio en disco? Lanza una excepción. ¿Los rayos cósmicos le impiden consultar la base de datos? Lanza una excepción. Pero si obtiene algunos datos no válidos desde el interior de su propio programa, no lance una excepción. Si su problema proviene de su propio código incorrecto, es mejor usar ASSERT para protegerse contra él. El manejo de excepciones es necesario para identificar problemas que el programa no puede manejar e informarles sobre el usuario, porque el usuario puede manejarlos. Pero los errores en su programa no son algo que el usuario pueda manejar, por lo que el bloqueo del programa indicará no mucho menos que "¡El valor de answer_to_life_and_universe_and_everything no es 42!

Detecte una excepción en la que pueda hacer algo útil con ella, como mostrar un cuadro de mensaje. Prefiero detectar una excepción una vez dentro de una función que de alguna manera maneja la entrada del usuario. Por ejemplo, el usuario presiona el botón "Annihilate all hunams", y dentro de la función annihilateAllHunamsClicked () hay un bloque try ... catch para decir "No puedo". Aunque la aniquilación de hunamkind es una operación compleja que requiere llamar a docenas y docenas de funciones, solo hay un intento ... atrapar, porque para un usuario es una operación atómica: hacer clic en un botón. Los controles de excepción en todas las funciones son redundantes y desagradables.

Además, no puedo recomendar lo suficiente que se familiarice con RAII, es decir, para asegurarse de que todos los datos que se inicializan se destruyen automáticamente. Y eso se puede lograr inicializando tanto como sea posible en la pila, y cuando necesite inicializar algo en el montón, use algún tipo de puntero inteligente. Todo lo que se inicialice en la pila se destruirá automáticamente cuando se produzca una excepción. Si usa punteros tontos de estilo C, corre el riesgo de pérdida de memoria cuando se lanza una excepción, porque no hay nadie que los limpie en caso de excepción (claro, puede usar punteros de estilo C como miembros de su clase, pero asegúrese de que sean cuidado en destructor).

Septagrama
fuente
Gracias por tu valiosa contribución. > "El manejo de excepciones es necesario para identificar problemas que el programa> no puede manejar e informarles sobre el usuario, porque el usuario puede> manejarlos" En lugar de lanzar una excepción, puedo devolver el valor de error y en la función de llamada puedo verificar el error y mostrar un mensaje que ayudará al usuario a manejarlo.
Umesha MS
Eche un vistazo a las preguntas frecuentes nuevamente, particularmente en los fragmentos de código tercero y cuarto de esta sección: parashift.com/c++-faq-lite/exceptions.html#faq-17.2 Las excepciones son excelentes, porque permiten que sus errores salgan a la luz desde cualquier profundidad de la pila de llamadas ... Es como si los códigos de error
fueran
2
@Septagram "Si su problema proviene de su propio código incorrecto, es mejor usar ASSERTs para protegerse contra él.": Una aserción no le permitirá continuar con tareas que no dependen de la rama con errores (solo porque su programa ha un error en una función no significa que no pueda hacer otras cosas útiles). No le permitirá utilizar registradores personalizados. Evitará los destructores de los que se beneficia mediante el uso de RAII (¿desea que los búferes de archivos se vacíen antes de que finalice el programa? ¡Mejor no omitir esos destructores con fcloseentonces!). No le dará un seguimiento de pila completo.
daemonspring
Algo que decir sobre RAII y "punteros tontos". El hecho de que utilice un "puntero tonto" no significa que no esté siguiendo a RAII. Recuerde que es solo cuando asigna recursos del sistema que debe asegurarse de que se desasignen. Un puntero es simplemente una variable en la pila que contiene una dirección de memoria. La asignación e inicialización de objetos no es necesaria para utilizar un "puntero tonto". Un "puntero tonto" puede simplemente contener la dirección de un objeto que está asignado en otro lugar, y está perfectamente bien de usar. (cont. en el siguiente comentario)
SeanRamey
Es posible que tenga un objeto Renderer que almacene la dirección del objeto Display en un puntero para dibujar cosas en la Pantalla, pero no puede usar un puntero inteligente porque un puntero inteligente destruirá el objeto Display cuando destruya el objeto Renderer , que no quieres que suceda. En este caso, las únicas opciones son un "puntero tonto" o una referencia.
SeanRamey
12

Las excepciones son útiles en una variedad de circunstancias.

Primero, hay algunas funciones en las que el costo de calcular la condición previa es tan alto que es mejor simplemente hacer el cálculo y abortar con una excepción si se encuentra que la condición previa no se cumple. Por ejemplo, no puede invertir una matriz singular, sin embargo, para calcular si es singular, calcula el determinante que es muy costoso: puede que tenga que hacerse dentro de la función de todos modos, así que "intente" invertir la matriz e informe un error si no puede lanzar una excepción. Esta es básicamente una excepción como uso de condiciones previas negativas .

Luego, hay otros casos en los que su código ya es complejo y es difícil pasar información de error por la cadena de llamadas. Esto se debe en parte a que C y C ++ tienen modelos de estructura de datos defectuosos: hay otras formas mejores, pero C ++ no las admite (como el uso de mónadas en Haskell). Este uso es básicamente que no me molestaría en hacerlo bien, así que lanzaré una excepción : no es la forma correcta, pero es práctica.

Luego está el uso principal de las excepciones: informar cuando las condiciones previas externas o invariantes, como recursos suficientes como memoria o espacio en disco, no están disponibles. En este caso, normalmente terminará el programa, o una subsección importante del mismo, y la excepción es una buena forma de transmitir información sobre el problema. Las Excepciones de C ++ fueron diseñadas para informar errores que impiden que el programa continúe .

Se sabe que el modelo de manejo de excepciones utilizado en la mayoría de los lenguajes modernos, incluido C ++, no funciona. Es demasiado poderoso. Los teóricos ahora han desarrollado mejores modelos que el modelo completamente abierto de "arrojar cualquier cosa" y "tal vez y tal vez no atraparlo". Además, el uso de información de tipo para clasificar excepciones no fue una muy buena idea.

Entonces, lo mejor que puede hacer es lanzar excepciones con moderación, cuando hay un error real y cuando no hay otra forma de lidiar con él y detectar excepciones lo más cerca posible del punto de lanzamiento .

Yttrill
fuente
15
¿Podría agregar algunos enlaces / referencias para "se sabe que el manejo de excepciones no funciona" y "Los teóricos ahora han desarrollado mejores modelos", por favor?
Juraj Blaho
1
Una cosa fundamental que a menudo debe conocerse al detectar excepciones, pero sobre la cual la mayoría de las excepciones no proporcionan datos útiles, es si el código que generó la excepción ha tenido algún efecto en el estado del sistema. ¿Alguno de los modelos de los teóricos se ocupa de esto?
supercat
@JurajBlaho Traté de encontrar los enlaces / referencias de los que estás hablando sin éxito. ¿Puedes editar su respuesta para agregarlas al final?
ForceMagic
Estoy de acuerdo con los ejemplos n. ° 1 y n. ° 3. Sin embargo, creo que si el código es demasiado complejo, por lo que está pensando en lanzar una excepción, tal vez necesite una refactorización. Afirmar también es una buena manera (incluso mejor) de verificar si el código se está ejecutando correctamente y si en realidad es menos costoso que la excepción. Recomiendo 2 capítulos (Programación asertiva y Cuándo usar excepciones) del libro: El programador pragmático, a cualquier persona interesada en este tema.
ForceMagic
1
Sin necesidad de enlaces. Usa cerebros. Puede lanzar una excepción que no se detecta. Eso es malo. Peor que goto, ya que goto al menos siempre va a alguna parte. La escritura estática no puede hacer frente a las especificaciones de excepción: no existe un sistema sano en el que las funciones polimórficas puedan tener una especificación de excepción porque depende de la instancia particular de la variable de tipo. Tenga en cuenta que estos se eliminaron del nuevo estándar C ++. El nuevo modelo se llama "continuaciones delimitadas": en.wikipedia.org/wiki/Delimited_continuation
Yttrill
4

Si su problema proviene de su propio código incorrecto, es mejor usar ASSERT para protegerse contra él. El manejo de excepciones es necesario para identificar problemas que el programa no puede manejar e informarles sobre el usuario, porque el usuario puede manejarlos. Pero los errores en su programa no son algo que el usuario pueda manejar, por lo que el bloqueo del programa no dirá mucho

No estoy de acuerdo con este aspecto de la respuesta aceptada . Una afirmación no es mejor que lanzar una excepción. Si las excepciones fueran adecuadas solo para errores en tiempo de ejecución (o "problemas externos"), ¿ std::logic_errorpara qué sirve ?

Un error lógico es casi por definición el tipo de condición que impide que un programa continúe. Si el programa es una construcción lógica y ocurre una condición fuera del dominio de esa lógica, ¿cómo puede continuar? ¡Reúna las entradas mientras pueda y haga una excepción!

No es que no haya arte previo. std::vector, por nombrar solo uno, arroja una excepción de error lógico, a saber std::out_of_range. Si usa la biblioteca estándar y no tiene un controlador de nivel superior para detectar excepciones estándar, aunque solo sea para llamar a qué () y salir (3), entonces sus programas están sujetos a una terminación repentina y silenciosa.

Una macro de aserción es una guardia mucho más débil. No hay recuperación. A menos que, es decir, no esté ejecutando una compilación de depuración, en cuyo caso no hay ejecución . La macro de aserción pertenece a una era en la que el cálculo era 6 órdenes de magnitud más lento que en la actualidad. Si se toma la molestia de probar errores lógicos, pero no usar esa prueba cuando sea necesario, en producción, ¡será mejor que tenga mucha confianza en su código!

La biblioteca estándar proporciona excepciones de errores lógicos y las emplea. Están ahí por una razón: porque ocurren errores lógicos y son excepcionales. El hecho de que C presente aserciones no es motivo para confiar en un mecanismo tan primitivo (y posiblemente inútil), cuando una excepción maneja el trabajo mucho mejor.

James K. Lowden
fuente
3

Mejor lectura para esto

Se ha hablado mucho sobre el manejo de excepciones durante la última década y media. Sin embargo, a pesar de un consenso general sobre cómo manejar adecuadamente las excepciones, sigue existiendo una división en el uso. El manejo inadecuado de excepciones es fácil de detectar, fácil de evitar y es una métrica de calidad de código (y desarrollador) simple. Sé que las reglas absolutas parecen cerradas o exageradas, pero como regla general, no deberías usar try / catch

http://codebetter.com/karlseguin/2010/01/25/don-t-use-try-catch/

Sushan M
fuente
3
¿No es ese artículo realmente tratando de decir, no use try/ catch {}?
Craig McQueen
2

Se incluye una verificación de excepción en el código cuando existe la posibilidad de obtener una excepción como resultado o en algún punto intermedio entre el problema.

2.Use el bloque try-catch solo con aquellos casos en los que sea necesario. El uso de cada bloque try-catch se suma a una verificación de condición adicional que ciertamente reduce la optimización del código.

3.Creo que _try_except es un nombre de variable válido ...

usuario550830
fuente
3
FYI, __try y __except son para el Manejo de excepciones estructurado (SEH) de Microsoft. msdn.microsoft.com/en-us/library/s58ftw19(v=vs.80).aspx
axw
0

La diferencia básica es:

  1. uno hace el manejo de errores por usted.
  2. uno es tu propio.

    • Por ejemplo, tiene una expresión que podría hacer 0 divide error. Usar try catch 1.le ayudará cuando ocurra un error. O si necesita una if a==0 then..en2.

    • Si no intenta capturar la excepción, no creo que sea más rápido, simplemente se omite, si errorocurre, será threwpara un controlador externo.

Entregarte a ti mismo significa que el problema no irá más lejos pues tienes ventaja en velocidad en muchos casos, pero no siempre.

Sugerir: manejarse usted mismo cuando sea simple y lógico.

pinichi
fuente
-3

Muchos puristas de C / C ++ desalientan las excepciones por completo. Las principales críticas son:

  1. Es lento - Por supuesto que no es realmente "lento". Sin embargo, comparado con c / c ++ puro, hay bastante sobrecarga.
  2. Introduce errores: si no maneja las excepciones correctamente, puede perder el código de limpieza en la función que genera la excepción.

En su lugar, verifique el valor de retorno / código de error cada vez que llame a una función.

avión de velocidad
fuente
15
Disparates. Primero, estamos hablando de C ++: no hay "C / C ++" y, por supuesto, los programadores que solo utilizan C se sentirían incómodos con las excepciones. "C / C ++ puro" no existe, y las excepciones son parte de "C ++ puro" - están en el Estándar. Puede escribir código de manejo de excepciones con errores y puede escribir código de retorno de errores con errores o código de estado de errores con errores; es engañoso caracterizar las excepciones como más propensas a errores en general. Lo siento, pero este es uno de los peores consejos que he visto en SO
Tony Delroy
1
Márcame si quieres, pero teniendo un fondo C, puedo decirte que yo y muchos otros nos abstenemos de usar excepciones por completo.
Avión de velocidad
4
1. Solo es lento cuando se lanza una excepción. Y las excepciones se llaman excepciones, porque solo se lanzan en circunstancias excepcionales. El programa que arroja más de 9000 excepciones por segundo lo está haciendo mal. 2. Si tiene cuidado de no realizar la administración de memoria con borrado nuevo puro, la limpieza se realizará por usted. La gestión de memoria de estilo C es mucho más propensa a errores que las excepciones. 3. Verificar el valor de retorno agrega muchas líneas adicionales de código, lo que lo hace menos legible y menos fácil de mantener.
Septagrama
3
1) dependiendo del compilador, esto puede no ser cierto. Y todas las excepciones agregan una buena cantidad de código de bytes que aumenta el tamaño del ejecutable y ralentiza las cosas. 2) Todo el tiempo se usa puro new-delete, no todo puede ser un objeto de pila. 3) La verificación de los valores de retorno puede ser más líneas de código, pero podría decirse que es más fácil de mantener. Puede que no esté de acuerdo, pero es una creencia razonable generalmente aceptada que las excepciones de C ++ son malas en muchos casos. Eche un vistazo a embedded-C ++ como ejemplo.
Speedplane
11 votos negativos y 8 votos positivos. Creo que está claro que si bien esta opinión no refleja el consenso general, hay un gran contingente de desarrolladores que desconfían de las excepciones por las razones que describí.
Avión de velocidad