exit abort return 區別


Exit abort return 三者區別
       exit() 結束當前進程/程序,在整個程序中,只要調用 exit ,就結束。return() 是當前函數返回,當然如果是在主函數main, 自然也就結束當前進程了,如果不是,那就是退回上一層調用。在多個進程時.如果有時要檢測某個進程是否正常退出的.就要用到這個進程的返回值。exit(0)表示進程正常退出. 返回 0; exit(1)表示進程非正常退出.返回 1。
       如果是C++的話,還是儘量避免使用exit。用exit 的時候任何自動變量和臨時變量的析構函數都不會被調用。如果是C 的話,關係不大。對於這種析構你可以用atexit 來指定一個退出時資源清理。也就是說可以利用atexit 函數爲exit 事件"掛接"另外的函數,這種"掛接"有點類似Windows 編程中的"鉤子"(Hook)。程序輸出"atexit 掛接的函數"後即終止,即便是我們不調用exit 函數,當程序本身退出時,atexit 掛接的函數仍然會被執行。atexit 可以被多次行,並掛接多個函數,這些函數的執行順序爲後掛接的先執行。
       當然,如果不想在出錯情況下處理錯誤信息的話,你可以用abort()來退出(例如在斷言中),而不能用exit,否則程序會產生一個系統錯誤提示,用了ABORT,退出當前處理過程時不會有錯誤提示出現。
       注意:在多線程程序中,不要在某個線程使用exit abort,這樣會導致整個進程退出。
C++ 中exit 的使用
1. 進程的結束
對於一個用C++寫的程序,被加載至內存後運行,最終走向死亡。程序的死亡大致有三種:
• 自然死亡,通常就是main()中的一個return 0;
• 自殺,即請求OS 將自己結束。有兩種方式:void exit(int status)和void abort(void)。
• 他殺,程序家族中的他殺行徑往往是由自己至親完成的,通常這個至親就是有親緣關係的進程。兇器(例如SIGKILL 信號)往往是由OS 直接或者間接提供的。
2. 程序死亡方式對對象析構的影響
       C++程序中大致有三種對象:全局對象、局部靜態對象、局部非靜態對象(自動對象)。例如:
#include <iostream>
using namespace std;
struct Foo
{
Foo(){ cout<<"Foo"<<endl; }
~Foo(){ cout<<"~Foo"<<endl; }
/* some other sources here */
};
Foo Global;
void quit();
int
main()
{
static Foo StaticLocal;
Foo Local;
// quit();
// abort();
return 0;
}
void quit()
{
Foo AnotherLocal;
exit(1);
}
     編譯運行這個程序,程序將正常退出。運行過程中,Global 對象在進入main之前首先被構造,其次是StaticLocal 和Local。main 函數退出之前,Local 和
StaticLocal 被析構,main 退出後Global 也將被析構。
如果將17 行處quit()的註釋去掉,我們將會看到4 個對象被構造,但卻只有兩個對象被析構,分別是Global 和StaticLocal 對象,其他兩個對象Local 和AnotherLocal 對象的析構函數將不會被調用。如果將18 行處abort()的註釋去掉(quit()被註釋),3 個對象對象被構造,但在程序退出之前沒有任何一個對象的析構函數被調用。
也就是說,正常情況下,所有類型的對象都將被析構;由exit 退出時只有非自動對象被析構;abort 被調用時,程序將直接退出,任何對象的析構函數都不會調用。
3. exit 做了什麼
介紹exit 之前,不得不提void atexit(void (*f)(void) )函數。atexit,描述了exit 裏面要做些什麼。使用atexit 可以向exit 註冊一系列的函數,這些函數在exit 中被調用,調用的順序與它們被註冊的順序相反。可以使用下面的代碼來驗證:
#include <iostream>
using namespace std;
void f1(){ cout<<"f1"<<endl; }
void f2(){ cout<<"f2"<<endl; }
int
main()
{
atexit(f1);
atexit(f2);
exit(1);
return 0;
}
       void exit(int status)被調用時,它首先調用全局的或者靜態的對象的析構函數,然後調用atexit 所註冊的函數。如果這些函數中的某一個再次調用exit,想想會有什麼後果?最後,exit 會將代表程序執行狀態的status“返回”(確切的說應該叫做傳遞,因爲exit 永遠不會返回調用方)給當前程序的父進程。值得一提的是,執行exit 結束程序,雖然自動對象的析構函數不被調用,但當程序結束時,OS 會將該程序佔用的資源全部釋放。這些資源包括該程序申請的內存(堆)、打開的文件句柄、管道(Unix/Linux)、socket 等。這樣一來,似乎那些析構函數不被調用並不會有什麼問題。但可惜這隻適用於單線程的程序。對於多線程程序來說,只有當整個進程結束時,它佔用的資源纔會被OS釋放,這時某個線程的exit 就有可能帶來麻煩(比如內存泄露)。
4. 使用 c++異常來取代exit()函數
使用C++提供的異常機制,可以很好的解決上面的問題。我們可以在需要exit 的地方拋出(throw)異常,然後在捕獲(catch)異常處調用exit(或者乾脆不用exit),這樣,所有需要的析構函數都將被調用。代碼:
例一:
#include <iostream>
#include <cstdlib>
using namespace std;
struct Foo
{
Foo(){ cout<<"Foo"<<endl; }
~Foo(){ cout<<"~Foo"<<endl; }
/* some other sources here */
};
struct except: public exception
{
const char* what() const throw()
{
return "except";
}
};
Foo Global;
void quit();
int
main()
{
try
{
static Foo StaticLocal;
Foo Local;
quit();
}
catch(const exception& e)
{
cerr<<e.what()<<endl;
exit(0);
}
return 0;
}
void quit()
{
Foo AnotherLocal;
// exit(1);
throw except();
}
輸出:
Foo
Foo
Foo
Foo
~Foo
~Foo
except
~Foo
~Foo
例二:
void main()
{
//初始化
...
try
{
processmail(...);
}
catch (int ret)
{
switch (ret) {
case e_initialization_failure: ...
case e_irrecoverable: ...
...
}
}
}
void processmail(...)
{
//初始化
...
if ( initializationerror )
{
throw(e_initialization_failure);
}
while ( !shutdown ) {
try
{
readmail(...)
}
catch (int ret)
{
switch (ret) {
case e_read_error:
//記錄錯誤信息
...
//試圖恢復
...
if ( recovered )
{
continue;
} else {
throw(e_irrecoverable);
}
break;
case ...
}
}
//繼續處理
...
}
//throw()可以用來取代缺少的返回碼
//但也要注意由此帶來的性能損失
throw(s_ok);
} // processmail()
void readmail(...)
{
...
//在此無須捕捉異常
nbytesavailable = readbytes(...)
...
}
int readbytes(...)
{
//讀取數據
if ( error )
{
throw(e_read_error);
}
return nbytesread;
}
發佈了28 篇原創文章 · 獲贊 6 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章