2012-02-22 164 views
9

运行的函数我有我想要运行每当我的程序退出的函数:你怎么退出在C++

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

如何注册当程序存在,它要运行,无论何时的为什么它退出 - 由于信号,exit()调用等?

+0

不可能在所有情况下 - 无论您是否希望它们,某些信号都会立即降低过程。 (例如堆栈溢出) – 2012-02-22 20:59:43

+0

什么操作系统? – 2012-02-22 21:34:06

回答

26

您可以在cstdlib头使用适当命名的std::atexit功能:

#include <cstdlib> 

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

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

系统将保持与atexit注册功能的堆栈,并呼吁他们各自在其注册的相反顺序时,无论是exit函数被调用,或程序从main返回。这种方式可以注册至少32个函数。

+0

在信号或呼叫到'abort' /'terminate'之后不会被调用。 (说:注册一个函数,在**正常进程终止时调用**) – 2012-02-22 21:01:38

+1

@BillyONeal这就是为什么我说“当调用'exit'函数或程序从'main'返回时出现的错误” – 2012-02-22 21:02:34

+0

感叹,我的错误:) – 2012-02-22 21:03:08

3

你可以把它放在具有全局实例的类的析构函数中。

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

但是像其他方法一样,你必须记住,如果你不能保证它仍然存在,你不能使用任何数据。全局对象的释放是以任意顺序完成的,所以基本上,你不能在foo()函数中使用std :: cout。 atexit()在这方面更糟糕,因为它是在销毁全局对象之前还是之后执行取决于编译器和编译器选项。

无论如何,你仍然必须正确处理信号。你必须选择要处理哪些信号,哪些不处理(你很可能不想处理SIGSEGV)。你不能逃避信号处理。并且请记住,信号可能随时中断您的程序(除非屏蔽),因此在更新过程中您的数据结构可能处于任意状态。

+0

我相信标准中有一种语言表示'cout'在任何地方都是有效的(定义为首先构造),但我现在找不到它。这也不适用于信号,'abort','terminate'等。 – 2012-02-22 21:00:35

+0

@BillyONeal:'abort'会产生一个信号(你必须选择处理或不处理,就像我说的那样),terminate是完全的不同的问题。另外,也许'std :: cout'总是存在,但是'abort()'的动作被定义为引发'SIGABRT'“,并且可能包括尝试在所有打开的流上产生'fclose()'。 fclose(stdout)将阻止std :: cout的工作。 – BatchyX 2012-02-22 23:00:44

+0

仅适用于具有'SIGABRT'这样的东西的系统。 'fclose(stdout)'可能会或可能不会导致std :: cout停止工作,具体取决于您的CRT。该标准没有定义标准控制台输入/输出/错误流与其C对应文件句柄之间的关系(如果有的话)。当我说'cout'总是有效的时候,我的意思是它不受'在不同的翻译单元中定义的非本地静态对象的初始化顺序是未定义的规则'的影响。也许我误解了你的答案。 – 2012-02-23 01:21:41

1

在进程退出后,重新获得控制权的唯一方法是在wait(2)之内(在Unix和类Unix操作系统中)。短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 2012-02-22 23:03:29

+0

这是一个非常有创意的答案(虽然它确实存在^ C将同时击中的评论者注意的问题)。如果需要,您可以忽略^ C,但是您将无法忽略^ \。 – IanPudney 2015-09-11 05:51:28

1

我回答作为一个Linux用户,但所有这一切都应该适用于Windows。

我有这个类似的问题,所以希望我可以总结以前的答案,并添加我的两分钱。

信号和abort()^C^Z可以被“截获”退出之前打电话给你的功能,推测可能与出口()。信号SIGQUIT AKA ^\SIGKILL没有键击不能被拦截。以下是使用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()在我上面的例子,必须在这里拍摄。如果正在运行的函数是只需运行一次的清理函数,则可以使用静态变量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流被关闭,只有自动存储变量没有被清理。