2012-02-22 21 views
9

Mam funkcji, które chcę uruchomić, gdy mój program wyjścia:Jak uruchomić funkcję wyjścia w C++

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

Jak mogę zarejestrować się on być uruchamiany, gdy istnieje program, niezależnie od tego, kiedy i dlaczego to się kończy - ze względu na sygnał, wywołanie exit() itp.?

+0

Niemożliwe we wszystkich przypadkach - niektóre sygnały spowalniają proces natychmiast, niezależnie od tego, czy chcesz, czy nie. (np. przepełnienie stosu) –

+0

Jaki system operacyjny? –

Odpowiedz

26

Można użyć wdzięcznej nazwie std::atexit funkcję w cstdlib nagłówka:

#include <cstdlib> 

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

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

System utrzymania stos funkcji zarejestrowanych atexit i nazywają je każdy w odwrotnej kolejności ich zarejestrowania gdy albo exit zostanie wywołana funkcja lub program powróci z wersji main. W ten sposób możesz zarejestrować co najmniej 32 funkcje.

+0

To nie jest wywoływane po sygnale lub wywołaniu 'abort' /' terminate'. (Mówi: zarejestruj funkcję, która ma być wywołana przy ** normalnym zakończeniu procesu **) –

+1

@BillyONeal, dlatego powiedziałem "kiedy albo zostanie wywołana funkcja' exit', albo program wróci z 'głównego' –

+0

Sigh, mój błąd :) –

3

Można umieścić go w destruktorze klasy z instancją globalną.

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

Jednak, podobnie jak w przypadku każdej innej metody, należy pamiętać, że nie można używać żadnych danych, jeśli nie można zagwarantować, że nadal istnieje. Dezalokacja obiektów globalnych odbywa się w dowolnej kolejności, więc w zasadzie nie można używać std :: cout w funkcji foo(). atexit() jest gorszy pod tym względem, ponieważ to, czy zostanie wykonane przed czy po zniszczeniu obiektów globalnych, zależy od opcji kompilatora i kompilatora.

W każdym razie nadal musisz poprawnie obsługiwać sygnały. Musisz wybrać sygnały do ​​obsługi, a których nie obsługiwać (najprawdopodobniej nie chcesz obsługiwać SIGSEGV). Nie można uciec od obsługi sygnału. Pamiętaj też, że sygnały mogą przerwać Twój program w dowolnym momencie (chyba że są maskowane), więc struktury danych mogą znajdować się w dowolnym stanie, w trakcie aktualizacji.

+0

Wierzę, że w standardzie jest język, który mówi, że 'cout' jest ważny wszędzie (zdefiniowany jako pierwszy), ale nie mogę go teraz znaleźć. To również nie działa dla sygnałów, 'abort',' zakończenie', itp. –

+0

@BillyONeal: 'abort' podnosi sygnał (który musisz wybrać, aby obsłużyć lub nie, tak jak powiedziałem)," zakończenie "jest całkowicie inna sprawa. Możliwe też, że 'std :: cout' zawsze istnieje, ale akcje' abort() 'są zdefiniowane w celu podniesienia' SIGABRT'' i mogą zawierać próbę wykonania 'fclose()' na wszystkich otwartych strumieniach ". fclose (stdout) uniemożliwi działanie std :: cout. – BatchyX

+0

Tylko w systemach, które mają coś takiego jak "SIGABRT". 'fclose (stdout)' może lub nie może spowodować, że 'std :: cout' przestanie działać, w zależności od twojego CRT. Standard nie definiuje relacji (jeśli jest) pomiędzy standardowymi strumieniami wejściowymi/wyjściowymi/błędów konsoli i ich odpowiednikami w pliku C. Kiedy mówię, że 'cout' jest zawsze poprawny, mam na myśli to, że nie wpływa na niego' kolejność inicjowania nielokalnych obiektów statycznych zdefiniowanych w różnych jednostkach tłumaczeniowych jest niezdefiniowana'. Być może źle zrozumiałem twoją odpowiedź. –

1

Jedynym sposobem (w systemach operacyjnych Unix i Unix) na odzyskanie kontroli po wyjściu z procesu jest uzyskanie dla niego wartości wait(2). Krótki z powerfail, kernel panic lub wymuszonym restarcie powinno działać:

#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

Przenosisz problem w inne miejsce: co się stanie, jeśli zabiję ten program? Jak napisano,^C na sterującym terminalu wyśle ​​SIGINT zarówno do rodzica, jak i dziecka. – BatchyX

+0

To jest naprawdę kreatywna odpowiedź (chociaż ma ona uwięziony przez komentatora problem, który^C trafi oba). Możesz zignorować^C, jeśli chcesz, ale nie będziesz mógł zignorować^\. – IanPudney

1

ja odpowiadam jako użytkownik Linuksa, ale wszystko to powinno mieć zastosowanie do okien.

Miałem to podobne pytanie, więc mam nadzieję, że mogę podsumować poprzednie odpowiedzi i dodać moje dwa centy.

Sygnały i abort(): ^C i ^Z można "przechwycić", aby wywołać funkcję przed wyjściem, prawdopodobnie przy pomocy exit(). Sygnały SIGQUIT AKA ^\ i , które nie mają naciśnięcia klawisza, nie mogą zostać przechwycone. Oto przykład użycia nagłówka csignal i 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; 
} 

Wyjście: Ponieważ użyłem exit() w moich przykładach powyżej, należy uważać tutaj. Jeśli uruchomiona funkcja jest funkcją czyszczącą, która musi zostać uruchomiona tylko raz, być może można użyć zmiennej statycznej has_run. Lub w powyższym przykładzie raise() sygnał, którego nie możesz przechwycić. Ale te mają tendencję do przychodzenia z rdzennymi wysypiskami, które są po prostu brudne. Twój wybór, tutaj. Przykładem następująco

#include <cstdlib> 
#include <iostream> 

using namespace std; 

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

    atexit(lam); 

    return 0; 
} 

zwrócić uwagę, że C++ 11 dodał quick_exit który ma towarzyszącego at_quick_exit które działają tak samo jak wyżej.Ale z quick_exit nie są wykonywane żadne zadania czyszczenia. Przeciwnie, z destruktorami obiektów są wywoływane i strumienie C są zamykane, a tylko automatyczne zmienne pamięci nie są oczyszczane.

Powiązane problemy