2012-02-22 57 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流被關閉,只有自動存儲變量沒有被清理。