Una vez me aconsejaron que un programa C ++ debería capturar todas las excepciones. El razonamiento dado en ese momento era esencialmente que los programas que permiten que surjan excepciones fuera de main()
entrar en un extraño estado zombie. Me dijeron esto hace varios años y, en retrospectiva, creo que el fenómeno observado se debió a la larga generación de vertederos de núcleos excepcionalmente grandes del proyecto en cuestión.
En ese momento, esto parecía extraño pero convincente. Era totalmente absurdo que C ++ "castigara" a los programadores por no detectar todas las excepciones, pero la evidencia ante mí parecía respaldar esto. Para el proyecto en cuestión, los programas que arrojaron excepciones no detectadas parecían entrar en un extraño estado zombie, o como sospecho que la causa era ahora, un proceso en medio de un volcado de núcleo no deseado es inusualmente difícil de detener.
(Para cualquiera que se pregunte por qué esto no era más obvio en ese momento: el proyecto generó una gran cantidad de resultados en múltiples archivos de múltiples procesos que efectivamente ocultaron cualquier tipo de aborted (core dumped)
mensaje y, en este caso particular, el examen post mortem de los volcados del núcleo no fue Es una técnica de depuración importante, por lo que no se pensó mucho en los volcados del núcleo. Los problemas con un programa generalmente no dependían del estado acumulado de muchos eventos a lo largo del tiempo por un programa de larga duración, sino más bien las entradas iniciales a un programa de corta duración (< 1 hora), por lo que era más práctico volver a ejecutar un programa con las mismas entradas de una compilación de depuración o en un depurador para obtener más información).
Actualmente, no estoy seguro de si existe alguna ventaja o desventaja importante de capturar excepciones con el único fin de evitar que las excepciones se vayan main()
.
La pequeña ventaja que se me ocurre al permitir que surjan excepciones pasadas main()
es que hace que el resultado std::exception::what()
se imprima en el terminal (al menos con los programas compilados con gcc en Linux). Por otro lado, esto es trivial de lograr al capturar en su lugar todas las excepciones derivadas std::exception
e imprimir el resultado std::exception::what()
y, si es deseable imprimir un mensaje de una excepción que no se deriva de std::exception
él, debe capturarse antes de partir main()
para imprimir. el mensaje.
La desventaja modesta en la que puedo pensar para permitir que surjan excepciones main()
es que se pueden generar volcados de núcleos no deseados. Para un proceso que utiliza una gran cantidad de memoria, esto puede ser una molestia y controlar el comportamiento de volcado de núcleo de un programa requiere llamadas a funciones específicas del sistema operativo. Por otro lado, si se desea un volcado de núcleo y una salida, esto se podría lograr en cualquier momento llamando std::abort()
y una salida sin volcado de núcleo se puede lograr en cualquier momento llamando std::exit()
.
Como anécdota, no creo haber visto nunca el what(): ...
mensaje predeterminado impreso por un programa ampliamente distribuido al fallar.
¿Cuáles son, si los hay, los argumentos fuertes a favor o en contra de permitir que las excepciones de C ++ surjan main()
?
Editar: Hay muchas preguntas generales sobre el manejo de excepciones en este sitio. Mi pregunta es específicamente sobre las excepciones de C ++ que no se pueden manejar y que han llegado hasta el final main()
; tal vez se pueda imprimir un mensaje de error, pero es un error de detención que se muestra inmediatamente.
fuente
Respuestas:
Un problema al permitir que las excepciones pasen de main es que el programa terminará con una llamada a la
std::terminate
que debe llamar el comportamiento predeterminadostd::abort
. Solo se define la implementación si el desbobinado de la pila se realiza antes de llamarterminate
para que su programa pueda finalizar sin llamar a un solo destructor Si tiene algún recurso que realmente necesitaba ser restaurado por una llamada de destructor, está en apuros ...fuente
std::abort
no lo hace y, por lo tanto, las afirmaciones fallidas tampoco. También vale la pena señalar que en los sistemas operativos modernos, el sistema operativo en sí limpiará muchos recursos (memoria, identificadores de archivos, ...) que están vinculados a la ID del proceso. Finalmente, también estaba insinuando "Defensa en profundidad": no es confiable esperar que todos los demás procesos sean a prueba de errores y siempre liberarán los recursos que adquirieron (sesiones, terminar de escribir archivos, ...) it ...WM_POWERBROADCAST
mensaje. Esto solo funciona si su computadora funciona con baterías (si está usando una computadora portátil o un UPS).La razón principal para no dejar escapar las excepciones
main
es porque, de lo contrario, pierde toda posibilidad de controlar cómo se informa el problema a sus usuarios.Para un programa que no está destinado a ser utilizado durante mucho tiempo o distribuido ampliamente, puede ser aceptable que se informen errores inesperados de cualquier manera que el sistema operativo decida hacerlo (por ejemplo, mostrando un diálogo de error directo en Windows )
Para los programas que vende, o que son proporcionados al público en general por una organización que tiene una reputación que mantener, generalmente es una mejor idea informar de una manera agradable que encontró un problema inesperado y tratar de ahorrar la mayor parte de datos del usuario como sea posible. No perder el trabajo de medio día de su usuario y no fallar inesperadamente, pero cerrar de forma semi-elegante generalmente es mucho mejor para su reputación comercial que la alternativa.
fuente
main()
tiene mucho que ver con el sistema operativo? El sistema operativo puede no saber nada de C ++. Supongo que está determinado por el código que el compilador inserta en algún lugar del programa.TL; DR : ¿Qué dice la especificación?
Un desvío técnico ...
Cuando se produce una excepción y ningún controlador está listo para ello:
std::terminate
se llama, que por defecto abortaEste último puede ser útil para errores muy poco frecuentes (porque reproducirlos es un proceso que desperdicia tiempo).
Si atrapar todas las excepciones o no es, en última instancia, una cuestión de especificación:
Para cualquier programa de producción, esto debe especificarse y usted debe seguir la especificación (y tal vez argumentar que se debe cambiar).
Para los programas agrupados rápidamente para ser utilizados solo por personas técnicas (usted, sus compañeros de equipo), tampoco está bien. Recomiendo dejar que se bloquee y configurar el entorno para obtener un informe o no, dependiendo de sus necesidades.
fuente
Una excepción que detecta le da la oportunidad de imprimir un buen mensaje de error o incluso tratar de recuperarse del error (posiblemente simplemente reiniciando la aplicación).
Sin embargo, en C ++ una excepción no contiene información sobre el estado del programa cuando se lanzó. Si lo detecta, todo ese estado se olvida, mientras que si deja que el programa se bloquee, el estado generalmente sigue ahí y se puede leer desde el volcado del programa, lo que facilita la depuración.
Entonces es una compensación.
fuente
En el momento en que sepa que tiene que abortar, continúe y llame
std::terminate
para reducir cualquier daño adicional.Si sabe que puede relajarse con seguridad, hágalo en su lugar. Recuerde que el desbobinado de la pila no está garantizado cuando nunca se detecta una excepción, así que atrape y vuelva a lanzar.
Si puede informar / registrar el error de manera segura mejor de lo que el sistema lo hará solo, continúe.
Pero asegúrese de no empeorar las cosas sin darse cuenta al hacerlo.
A menudo es demasiado tarde para guardar datos cuando detecta un error irrecuperable, aunque depende del error específico.
De todos modos, si su programa está escrito para una recuperación rápida, simplemente matarlo podría ser la mejor manera de terminarlo, incluso si es solo un apagado normal.
fuente
Chocar con gracia es algo bueno la mayor parte del tiempo, pero hay compensaciones. A veces es bueno chocar. Debo mencionar que estoy pensando principalmente en depurar a gran escala. Para un programa simple, aunque esto podría ser útil, no es tan útil como lo es con un programa muy complejo (o varios programas complejos que interactúan).
No desea bloquearse en público (aunque esto es realmente inevitable: los programas se bloquean, y un programa que no se bloquea verdaderamente matemáticamente verificable no es de lo que estamos hablando aquí). Piensa en BSODing de Bill Gates en medio de una demostración, ¿mal, verdad? No obstante, podemos tratar de descubrir por qué nos estrellamos y no volver a estrellarnos de la misma manera.
Encendí una función de Informe de errores de Windows que crea volcados de memoria locales en excepciones no controladas. Funciona de maravilla si tiene los archivos de símbolos asociados con su compilación (bloqueada). Curiosamente, porque configuré esta herramienta, quiero bloquear más , porque aprendo de cada bloqueo. Entonces puedo arreglar los errores y fallar menos.
Entonces, por ahora, quiero que mis programas lleguen a la cima y se bloqueen. En el futuro, podría querer comer con gracia todas mis excepciones, pero no si puedo hacer que funcionen para mí.
Puede leer más acerca de los volcados por caída locales aquí si está interesado: Recopilar volcados en modo de usuario
fuente
En última instancia, si una excepción aparece más allá de main (), bloqueará su aplicación y, en mi opinión, una aplicación nunca debería bloquearse. Si está bien bloquear una aplicación en un lugar, ¿por qué no en cualquier lugar? ¿Por qué molestarse con un manejo excepcional? (Sarcasmo, realmente no sugiere esto ...)
Es posible que tenga un intento / captura global que imprime un mensaje elegante que le dice al usuario que se ha producido un error irrecuperable y que necesita enviar un correo electrónico a TI al respecto o algo similar, pero el bloqueo es completamente no profesional e inaceptable. Es trivial prevenir y puede informar a un usuario sobre lo que acaba de suceder y cómo solucionarlo para que no vuelva a suceder.
Estrellarse es estrictamente hora de aficionados.
fuente
main
. Si no sabes cómo manejarlo, fingir que lo haces es obviamente un error realmente horrible.