2012-02-22 3 views
9

У меня есть функция, которую я хочу работать, когда моя программа выходит:Как запустить функцию на выходе в C++

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

Как зарегистрировать его для запуска всякий раз, когда программа существует, независимо от того, когда и почему он выходит - из-за вызова сигнала, выхода() и т. д.?

+0

Невозможно во всех случаях - некоторые сигналы немедленно прекращают процесс, хотите ли вы их или нет. (например, переполнение стека) –

+0

Какая операционная система? –

ответ

26

Вы можете использовать метко назвали std::atexit функции в cstdlib заголовке:

#include <cstdlib> 

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

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

система будет поддерживать стек функции, зарегистрированные с atexit и называть их каждые в обратном порядке их регистрации, когда либо exit вызывается функция, или программа возвращается с main. Таким образом, вы можете зарегистрировать не менее 32 функций.

+0

Это не вызывается после сигнала или вызова 'abort' /' terminate'. (Говорит: зарегистрируйте функцию, которая будет вызываться при ** нормальном завершении процесса **) –

+1

@BillyONeal, поэтому я сказал: «Когда вызывается функция' exit' или программа возвращается из 'main' ' –

+0

Sigh, моя ошибка :) –

3

Вы можете поместить его в деструктор класса с глобальным экземпляром.

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

Но, как и любой другой метод, вы должны помнить, что вы не можете использовать любые данные, если вы не можем гарантировать, что она до сих пор существует. Освобождение глобальных объектов выполняется в произвольном порядке, поэтому в принципе вы не можете использовать std :: cout в функции foo(). atexit() хуже в этом отношении, потому что независимо от того, выполняется ли он до или после уничтожения глобальных объектов, зависит от параметров компилятора и компилятора.

И все равно вам все равно придется правильно обращаться с сигналами. Вы должны выбрать, какие сигналы обрабатывать и которые не обрабатывать (вы, скорее всего, не хотите обрабатывать SIGSEGV). Вы не можете избежать обработки сигнала. И помните, что сигналы могут прерывать вашу программу в любое время (если не замаскировано), поэтому ваши структуры данных могут находиться в произвольном состоянии, в середине обновления.

+0

Я считаю, что в стандарте есть язык, который говорит, что 'cout' действителен всюду (определенный для построения в первую очередь), но я не могу найти его прямо сейчас. Это также не работает для сигналов, 'abort',' terminate' и т. Д. –

+0

@BillyONeal: 'abort' вызывает сигнал (который вы должны выбрать для обработки или нет, как я уже сказал),' terminate' является полностью другой пункт. Кроме того, возможно, 'std :: cout' всегда существует, но действия' abort() 'определены для повышения' SIGABRT' и могут включать попытку эффекта 'fclose()' для всех открытых потоков ". fclose (stdout) предотвратит работу std :: cout. – BatchyX

+0

Только для систем, имеющих такую ​​вещь, как 'SIGABRT'. 'fclose (stdout)' может или не может заставить 'std :: cout' перестать работать, в зависимости от вашего CRT. Стандарт не определяет отношения (если они есть) между стандартными потоками ввода/вывода/ошибок консоли и их обратными файлами C-файлов. Когда я говорю, что 'cout' всегда действителен, я имею в виду, что на него не влияет« порядок инициализации нелокальных статических объектов, определенных в разных единицах перевода, это неопределенное »правило. Возможно, я неправильно понял ваш ответ. –

1

Единственный способ (в Unix и Unix-подобных операционных системах) восстановить управление после выхода процесса - wait(2). Короткие из powerfail, паники ядра, или принудительной перезагрузки, это должно работать:

#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

Вы перемещаете проблему в другом месте: что произойдет, если я убью эту программу? Как написано,^C на управляющем терминале отправит SIGINT как родительскому, так и дочернему. – BatchyX

+0

Это действительно творческий ответ (хотя у него есть замечание, отмеченное комментатором, что^C поразит оба). Вы можете игнорировать^C в этом, если хотите, но вы не сможете игнорировать^\. – IanPudney

1

я отвечаю, как пользователь Linux, но все это должно относиться к окнам.

У меня был этот похожий вопрос, поэтому, надеюсь, я смогу подвести предыдущие ответы и добавить свои два цента.

Сигналы и abort(): ^C и ^Z могут быть «перехвачены», чтобы вызвать функцию перед выходом, предположительно с выходом(). Сигналы SIGQUIT AKA ^\ и SIGKILL, которые не имеют нажатия клавиши, не могут быть перехвачены. Ниже приведен пример использования заголовка csignal и лямбда 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; 
} 

Выход: Так как я использовал exit() в моих примерах выше, необходимо соблюдать осторожность здесь. Если выполняемая функция является функцией очистки, которая должна выполняться только один раз, возможно, может использоваться статическая переменная has_run. Или в приведенном выше примере raise() сигнал, который вы не можете перехватить. Но те, как правило, приходят с основными отвалами, которые просто чувствуют себя грязными. Ваш выбор, здесь. Пример следующим образом

#include <cstdlib> 
#include <iostream> 

using namespace std; 

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

    atexit(lam); 

    return 0; 
} 

принять к сведению, что C++ 11 добавили quick_exit, который имеет сопутствующую at_quick_exit, которые действуют так же, как указано выше.Но с quick_exit не выполняются задачи очистки. Напротив, с exit вызываются объекты-деструкторы и потоки C закрываются, и только автоматические переменные хранилища не очищаются.

Смежные вопросы