2012-02-22 18 views
9

que tienen una función que quiero correr siempre que mis salidas del programa:¿Cómo se ejecuta una función de salida en C++

void foo() { 
    std::cout<< "Exiting" << std::endl; 
} 

Como me registro para ejecutarse cada vez que el programa existe, independientemente del momento y por qué sale - debido a señal, llamada de salida(), etc.

+0

No es posible en todos los casos: algunas señales reducen el proceso de inmediato, lo quieras o no. (por ejemplo, desbordamiento de pila) –

+0

¿Qué sistema operativo? –

Respuesta

26

Puede utilizar el std::atexit función mejor nombrado en el cstdlib cabecera:

#include <cstdlib> 

void exiting() { 
    std::cout << "Exiting"; 
} 

int main() { 
    std::atexit(exiting); 
} 

El sistema mantendrá una pila de funciones registradas con atexit y llame a cada uno en el orden inverso al de su registro cuando sea el exit se llama a la función, o el programa regresa de main. Puede registrar al menos 32 funciones de esta manera.

+0

Eso no se llama después de una señal o una llamada a 'abort' /' terminate'. (Dice: registrar una función para llamar a ** terminación de proceso normal **) –

+1

@BillyONeal por lo que dije "cuando se llama a la función' exit' o el programa regresa de 'main'" –

+0

Suspiro, mi error :) –

3

Podría ponerlo en el destructor de una clase con una instancia global.

class SomeGlobalStuff { 
    ~SomeGlobalStuff() { 
      foo(); 
    } 
    static SomeGlobalStuff instance; 
}; 
// putting this in a single compilation unit. 
SomeGlobalStuff SomeGlobalStuff::instance instance; 

Pero al igual que cualquier otro método, hay que recordar que no se puede utilizar ningún dato si no podemos garantizar que todavía existe. La desasignación de objetos globales se realiza en un orden arbitrario, por lo que básicamente no se puede usar std :: cout en la función foo(). atexit() es peor en este sentido, porque si se ejecuta antes o después de la destrucción de los objetos globales depende del compilador y las opciones del compilador.

Y, de todos modos, todavía tiene que manejar las señales correctamente. Debe elegir qué señales manejar y cuáles no manejar (lo más probable es que no desee manejar SIGSEGV). No puedes escapar el manejo de la señal. Y recuerde que las señales pueden interrumpir su programa en cualquier momento (a menos que esté enmascarado) para que sus estructuras de datos estén en un estado arbitrario, en el medio de una actualización.

+0

Creo que hay un lenguaje en el estándar que dice 'cout' es válido en todas partes (definido para ser construido primero) pero no puedo encontrarlo ahora. Esto tampoco funciona para señales, 'abort',' terminate', etc. –

+0

@BillyONeal: 'abort' genera una señal (que debes elegir manejar o no, como dije),' terminate' es un completo materia diferente. Además, tal vez 'std :: cout' siempre existe, pero las acciones' abort() 'están definidas para aumentar' SIGABRT' "y pueden incluir un intento de efectuar' fclose() 'en todas las secuencias abiertas". fclose (stdout) evitará que std :: cout funcione. – BatchyX

+0

Solo en sistemas que tienen algo como 'SIGABRT'. 'fclose (stdout)' puede o no provocar que 'std :: cout' deje de funcionar, dependiendo de su CRT. El estándar no define la relación (si existe) entre las transmisiones de entrada/salida/error de la consola estándar y sus manejos de archivo de contraparte en C. Cuando digo que 'cout' siempre es válido, quiero decir que no se ve afectado por el' el orden de inicialización de los objetos estáticos no locales definidos en diferentes unidades de traducción no está definido '. Quizás malentendí tu respuesta. –

1

La única forma (en sistemas operativos Unix y Unix) de recuperar el control después de que finaliza un proceso es en wait(2). A falta de una powerfail, pánico del kernel, o reinicio forzado, esto debería funcionar:

#include <sys/types.h> 
#include <sys/wait.h> 
#include <iostream> 

int AtExit() { 
    pid_t pid = fork(); 
    if(pid < 0) return pid; 
    if(pid == 0) return pid; 
    pid = waitpid(pid, 0, 0); 
    return pid; 
} 

int main() { 
    if(AtExit()) { 
    std::cout << "Exiting\n"; 
    return 0; 
    } 

    std::cout << 7 << "\n"; 
} 
+1

Estás trasladando el problema a otra parte: ¿qué ocurre si elimino este programa? Como está escrito,^C en la terminal de control enviará un SIGINT a padres e hijos. – BatchyX

+0

Esta es una respuesta realmente creativa (aunque tiene el problema mencionado por el comentarista de que^C afectará a ambos). Puede ignorar^C en esto si lo desea, pero no podrá ignorar^\. – IanPudney

1

estoy respondiendo como el de Linux, pero todo esto debe aplicarse a las ventanas.

Tuve esta pregunta similar, así que espero poder resumir las respuestas anteriores y agregar mis dos centavos.

Señales y abort(): ^C y ^Z pueden ser "interceptados" para llamar a su función antes de salir, presumiblemente con exit(). Las señales SIGQUIT AKA ^\ y SIGKILL que no tienen golpe de tecla no pueden ser interceptadas. Aquí hay un ejemplo para usar el encabezado csignal y una lambda C++.

#include <iostream> 
#include <csignal> 
#include <cstdlib> 

using namespace std; 

int main() 
{ 
    //signal requires lam take an int parameter 
    //this parameter is equal to the signals value 
    auto lam = 
     [] (int i) { cout << "aborting" << endl; exit(0); }; 

    //^C 
    signal(SIGINT, lam); 
    //abort() 
    signal(SIGABRT, lam); 
    //sent by "kill" command 
    signal(SIGTERM, lam); 
    //^Z 
    signal(SIGTSTP, lam); 


    while(1) 
    { 
    } 

    return 0; 
} 

Salir: Desde que utiliza exit() en mis ejemplos anteriores, se debe tener cuidado aquí. Si la función que se está ejecutando es una función de limpieza que solo necesita ejecutarse una vez, quizás se pueda usar una variable estática has_run. O en el ejemplo anterior, raise(), una señal que no puede interceptar. Pero esos tienden a venir con vertederos que simplemente se sienten sucios. Tu elección, aquí. Un ejemplo sigue

#include <cstdlib> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    //called with no parameters 
    auto lam = []() { cout << "at exit"; }; 

    atexit(lam); 

    return 0; 
} 

toman nota de que C++ 11 añade un quick_exit que tiene una at_quick_exit acompaña, que actúan igual que el anterior.Pero con quick_exit no se realizan tareas de limpieza. Por el contrario, con exit se invocan destructores de objeto y se cierran flujos de C, con solo variables de almacenamiento automático que no se limpian.

Cuestiones relacionadas