abortar, terminar o salir?

112

¿Cuál es la diferencia entre esos tres y cómo terminaré el programa en caso de una excepción que no pueda manejar correctamente?

No hay nada que podemos hacer
fuente
3
Esto no es un duplicado, sino un subconjunto con algunas buenas respuestas stackoverflow.com/questions/397075/… ¡ y también estaba etiquetado como C ++!
Ellie Kesselman
std::abortes razonable si una excepción no se puede resolver en un destructor.
Daniel
1
Para obtener más información acerca de std::terminateestos artículos en el excelente blog de C ++ de Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

Respuestas:

3

Mi consejo sería no utilizar ninguno de ellos. En cambio, catchlas excepciones no se pueden manejar en main()y simplemente returndesde allí. Esto significa que tiene la garantía de que el desenrollado de la pila se realiza correctamente y se llaman todos los destructores. En otras palabras:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}
SS Anne
fuente
8
@Neil: Básicamente de acuerdo, pero las excepciones que el programa no puede manejar deben informarse y volver a lanzarse. Deje que la aplicación se bloquee.
John Dibling
13
Para asegurarse de que la pila se desenrolle, siempre debe capturar en main. Pero volvería a lanzar desde la captura. Como algunos sistemas operativos tienen la capacidad de invocar automáticamente la infraestructura de depuración si ha compilado en debug.
Martin York
5
Las excepciones que no son detectadas ni siquiera por un controlador de nivel superior podrían invocar una función de informes del sistema que descarga el proceso y carga el informe de excepciones para la atención de los desarrolladores, como Informe de errores de Windows, informes de errores de Mac OS X y registros de errores de aplicaciones de iPhone.
JBRWilkinson
6
@John La razón por la que me sorprende es que, aunque tiene perfecto sentido a la luz de cómo se implementan realmente las excepciones, rompe la abstracción de que una excepción "se propaga hacia arriba en la pila" hasta que se encuentra un controlador adecuado (o termina llamado). Y las abstracciones con fugas, aunque a menudo inevitables, son necesariamente sorprendentes cuando se encuentran.
Tyler McHenry
11
-1 porque esto no responde a la mitad de la pregunta. "¿Cuál es la diferencia entre [abortar, terminar o salir?]" Esta es una mejor respuesta: stackoverflow.com/a/397081/353094 También stackoverflow.com/a/2820407/353094 es una gran respuesta.
leetNightshade
149
  • abortar indica un final "anormal" del programa, y ​​genera la señal POSIX SIGABRT, lo que significa que se invocará cualquier controlador que haya registrado para esa señal, aunque el programa terminará después de las palabras en ambos casos. Por lo general, lo usaría aborten un programa C para salir de un caso de error inesperado donde es probable que el error sea un error en el programa, en lugar de algo como una entrada incorrecta o una falla de la red. Por ejemplo, podría aborthacerlo si se descubriese que una estructura de datos tiene un puntero NULL cuando, lógicamente, eso nunca debería suceder.

  • exit indica un final "normal" del programa, aunque esto aún puede indicar una falla (pero no un error). En otras palabras, exitpodría aparecer un código de error si el usuario proporcionó una entrada que no se pudo analizar o un archivo no se pudo leer. Un código de salida de 0 indica éxito. exittambién, opcionalmente, llama a los controladores antes de que finalice el programa. Estos están registrados con las funciones atexity on_exit.

  • std :: terminate es lo que se llama automáticamente en un programa C ++ cuando hay una excepción no controlada. Este es esencialmente el equivalente de C ++ abort, asumiendo que está informando todos sus errores excepcionales mediante la publicación de excepciones. Esto llama a un controlador establecido por la std::set_terminatefunción, que de forma predeterminada simplemente llama abort.

En C ++, generalmente desea evitar llamadas aborto exiterrores, ya que es mejor lanzar una excepción y dejar que el código más arriba en la pila de llamadas decida si finalizar el programa es apropiado o no. Si lo usa o no exitpara el éxito es una cuestión de circunstancias, si tiene sentido o no finalizar el programa en otro lugar que no sea la declaración de retorno en main.

std::terminatedebe considerarse una herramienta de informe de errores de última hora, incluso en C ++. El problema std::terminatees que el controlador de terminación no tiene acceso a la excepción que no se controló, por lo que no hay forma de saber qué fue. Por lo general, es mucho mejor envolver la totalidad de main en un try { } catch (std::exception& ex) { }bloque. Al menos entonces puede reportar más información sobre las excepciones derivadas de std::exception(aunque, por supuesto, las excepciones que no derivan de ellas std::exceptionterminarían sin ser manejadas).

Envolver el cuerpo de mainin try { } catch(...) { }no es mucho mejor que configurar un controlador de terminación, porque nuevamente no tiene acceso a la excepción en cuestión. Editar: según la respuesta de Neil Butterworth, hay un beneficio en que la pila se desenrolla en este caso, lo que (algo sorprendentemente) no es cierto para una excepción no manejada.

Tyler McHenry
fuente
10
¿Puedes actualizar esta respuesta con información de C ++ 11? Parece que ahora hay formas de obtener la excepción en el catch (...) y en el controlador de terminación.
Klaim
1
En C ++ el manejador de terminar hace tener acceso a través de la excepción std::current_exception(). Véase el ejemplo aquí: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm
No importa que pueda obtener la excepción actual, ya que no puede inspeccionarla. Todo lo que puedes hacer es volver a tirarlo.
seattlecpp
2
@seattlecpp puede volver a lanzarlo y obtener una referencia a él, que luego puede inspeccionar
gpeche
16

std :: abort y std :: exit (y más: std :: _ Exit, std :: quick_exit) son solo funciones de nivel inferior. Los usa para decirle al programa lo que quiere que haga exactamente: qué destructores (y si) llamar, qué otras funciones de limpieza llamar, qué valor devolver, etc.

std :: terminate es una abstracción de nivel superior: se la llama (ya sea por tiempo de ejecución o por usted) para indicar que ocurrió un error en el programa y que por alguna razón no es posible manejarlo lanzando una excepción. La necesidad de esto generalmente ocurre cuando ocurre un error en el mecanismo de excepción, pero puede usarlo en cualquier momento cuando no desee que su programa continúe más allá del error dado. Compilé la lista completa de situaciones cuando std :: terminate es llamado en mi publicación. No se especifica qué hace std :: terminate, porque usted tiene el control de ello. Puede configurar el comportamiento registrando cualquier función. Las limitaciones que tiene son que la función no puede regresar al sitio del error y no puede salir a través de una excepción, pero técnicamente incluso puede iniciar su bomba de mensajes adentro. Para ver la lista de cosas útiles que puede hacer adentro, vea mi otra publicación .

En particular, tenga en cuenta que std :: terminate se considera un controlador de excepciones en contextos donde se llama a std :: terminate debido a una excepción lanzada que no se pudo manejar, y puede verificar cuál fue la excepción e inspeccionarla usando C ++ 11 usando std :: rethrow_exception y std :: current_exception. Todo está en mi publicación .

Andrzej
fuente
¿Es recomendable tener un controlador de limpieza en caso de que el programa finalice debido a las señales del sistema? Por ejemplo, el acceso inválido a la memoria conduce a la generación de una señal SIGSEGV. En este caso, ¿es bueno dejar que el programa termine y tener el archivo central o registrar un manejador de señales para hacer la limpieza? ¿Existe alguna preocupación de realizar una limpieza mientras se manejan las señales del sistema en comparación con hacer una limpieza mientras se maneja std :: terminate?
kartik trivikram
12

salida_rápida () !

Si su programa tiene varios subprocesos, exit()lo más probable es que la llamada produzca un bloqueo porque std::threadse intentará destruir los objetos globales / estáticos sin salir de sus subprocesos.

Si desea devolver un código de error y salir del programa (más o menos) normalmente, llame quick_exit()a los programas de subprocesos múltiples. Por terminación anormal (sin posibilidad de que especifique el código de error), abort()o std::terminate()puede ser llamado.

Nota: MSVC ++ no admite quick_exit () hasta la versión 2015.

Serge Rogatch
fuente
4
  • terminar te deja la posibilidad de registrar lo que sucederá cuando se llame. Debería ser uno de los otros dos.
  • exit es una salida normal que permite especificar un estado de salida. Se ejecutan los controladores registrados por at_exit ()
  • abortar es una salida anormal. Lo único que se ejecuta es el manejador de señales para SIGABRT.
Un programador
fuente
4
  • terminate () se llama automáticamente cuando ocurre una excepción que no se puede manejar. Por defecto, terminate () llama a abort (). Puede establecer un identificador personalizado con la función set_terminate ().

    abort () envía la señal SIGABRT.

    exit () no es necesariamente algo malo. Sale con éxito de la aplicación y llama a las funciones atexit () en orden LIFO. Normalmente no veo esto en aplicaciones C ++, sin embargo, lo veo en muchas aplicaciones basadas en Unix donde envía un código de salida al final. Por lo general, una salida (0) indica una ejecución exitosa de la aplicación.

Daniel
fuente
8
¡Fracasado! Tanto en Unix como en DOS, exit (0) indica éxito y cualquier otro valor pasado a exit () indica falla, ¡no al revés!
Richard Barrell