简体   繁体   English

如何在C ++中的退出时运行函数

[英]How do you run a function on exit in C++

I have a function that I want to run whenever my program exits: 我有一个要在程序退出时运行的函数:

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

How do I register it to be run whenever the program exists, regardless of when and why it exits - due to signal, exit() call, etc? 我如何注册它以便在程序存在时运行,无论何时,为什么退出(由于信号,exit()调用等)?

You can use the aptly named std::atexit function in the cstdlib header: 您可以在cstdlib标头中使用恰当命名的std::atexit函数:

#include <cstdlib>

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

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

The system will maintain a stack of functions registered with atexit and call them each in the reverse order of their registration when either the exit function is called, or the program returns from main . 系统将维护一堆在atexit注册的函数,并在调用exit函数或程序从main返回时以与注册相反的顺序调用它们。 You can register at least 32 functions this way. 您可以这样注册至少32个功能。

I am answering as a Linux user, but all of this should apply to windows. 我以Linux用户的身份回答,但所有这些都应适用于Windows。

I had this similar question, so hopefully I can sum up previous answers and add my two cents. 我也有类似的问题,所以希望我可以总结一下以前的答案,并加两分钱。

Signals and abort() : ^C and ^Z can be "intercepted" to call your function before exiting, presumably with exit(). 信号和abort() :可以在退出之前“拦截” ^C^Z来调用您的函数,大概是通过exit()进行。 Signals SIGQUIT AKA ^\\ and SIGKILL which has no key stroke cannot be intercepted. 没有按键击的信号SIGQUIT AKA ^\\SIGKILL Here's an example for using the csignal header and a C++ lambda. 这是使用csignal标头和C ++ lambda的示例。

#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: Since I used exit() in my examples above, care must be taken here. 退出:由于我在上面的示例中使用了exit() ,因此在此必须小心。 If the function being run is a clean-up function that only needs to run once, perhaps a static variable has_run could be used. 如果正在运行的函数是仅需要运行一次的清理函数, has_run可以使用静态变量has_run Or in the example above, raise() a signal that you can't intercept. 或者在上面的示例中, raise()一个您无法拦截的信号。 But those tend to come with core dumps which just feels dirty. 但是那些往往带有核心转储,感觉很脏。 Your choice, here. 您的选择,在这里。 An example follows 一个例子如下

#include <cstdlib>
#include <iostream>

using namespace std;

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

    atexit(lam);

    return 0;
}

Take note that c++11 added a quick_exit which has an accompanying at_quick_exit which act the same as above. 请注意,c ++ 11添加了一个quick_exit ,它具有一个伴随的at_quick_exit ,其作用与上述相同。 But with quick_exit no clean up tasks are performed. 但是使用quick_exit不会执行清理任务。 In contrast, with exit object destructors are called and C streams are closed, with only automatic storage variables not getting cleaned up. 相反,使用exit对象时,将调用析构函数,并关闭C流,而不会清除自动存储变量。

You could put it in the destructor of a class with a global instance. 您可以将其放在具有全局实例的类的析构函数中。

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

But like any other method, you have to remember that you cannot use any data if you cannot garantee that it still exists. 但是,与任何其他方法一样,您必须记住,如果不能保证任何数据仍然存在,就不能使用任何数据。 Deallocation of global objects is done in a arbitrary order, so basically, you cannot use std::cout in the foo() function. 全局对象的取消分配以任意顺序完成,因此,基本上,您不能在foo()函数中使用std :: cout。 atexit() is worse in this regard, because whether it executes before or after destruction of global objects depends on the compiler and compiler options. 就此而言,atexit()更糟,因为它是在销毁全局对象之前还是之后执行取决于编译器和编译器选项。

And anyway, you still have to handle signals correctly. 而且无论如何,您仍然必须正确处理信号。 You have to choose which signals to handle and which to not handle (you most likely don't want to handle SIGSEGV). 您必须选择要处理和不处理的信号(您很可能不想处理SIGSEGV)。 You cannot escape signal handling. 您无法逃避信号处理。 And remember that signals may interrupt your program at any time (unless masked) so your data structures might be in a arbitrary state, in the middle of an update. 请记住,信号可能会随时中断程序(除非被屏蔽),因此在更新过程中,数据结构可能处于任意状态。

The only way (in Unix and Unix-like operating systems) to regain control after a process exits is to wait(2) for it. 在进程退出后重新获得控制权的唯一方法(在Unix和类似Unix的操作系统中)是wait(2) Short of a powerfail, kernel panic, or forced reboot, this should work: 如果没有电源故障,内核崩溃或强制重启,这应该可以工作:

#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";
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM