exit() 濫用導致的維護地獄C 風格代碼常見模式:
void ReadBytes(...) {
if (error) {
printf("error!!\n");
exit(-1); // 直接終止進程
}
}
缺點:
exit() 讓上層無法捕獲處理throw 取代 exit(),分層 catch 處理exit(code) 替換為 throw ERROR_CODEmain)捕獲 int 異常
// main.cpp
void main() { Init(); ProcessMail(); }
// mail.cpp
void ProcessMail() {
if (initErr) { printf("init fail"); exit(-1); }
while (!shutdown) { ReadMail(); }
}
void ReadMail() { n = ReadBytes(); ... }
// io.cpp
int ReadBytes() {
if (err) { printf("read err"); exit(-1); }
return n;
}
// 定義錯誤碼枚舉
enum ErrorCode {
E_INIT_FAILED = -1,
E_READ_ERROR = -2,
E_IRRECOVERABLE = -3,
S_OK = 0
};
// main.cpp
int main() {
try {
Init();
ProcessMail();
} catch (int code) {
switch (code) {
case E_INIT_FAILED: Log("初始化失敗"); break;
case E_IRRECOVERABLE: Log("致命錯誤,退出"); break;
}
return code;
}
return 0;
}
// mail.cpp
void ProcessMail() {
if (initErr) throw E_INIT_FAILED;
while (!shutdown) {
try { ReadMail(); }
catch (int code) {
if (code == E_READ_ERROR) {
Log("讀取錯誤,嘗試恢復...");
if (Recover()) continue;
throw E_IRRECOVERABLE;
}
throw; // 重拋未知錯誤
}
}
throw S_OK; // 正常結束也用異常傳遞(舊代碼無返回值時)
}
// io.cpp
int ReadBytes() {
if (err) throw E_READ_ERROR;
return n;
}
| 維度 | exit() | throw + catch |
|---|---|---|
| 資源清理 | 無(繞過解構子) | 自動呼叫沿途物件解構子 (RAII) |
| 錯誤上下文 | 僅錯誤碼 | 可攜帶任意型別異常物件 (含堆疊追蹤) |
| 恢復能力 | 無 | 中間層可 catch 處理後 continue/retry |
| 模組邊界 | 穿透所有層級 | 可在模組介面統一轉換/封裝錯誤 |
| 舊代碼改動 | 無 | 僅替換 exit 行,無需改函數簽名 |
int/enum,新代碼建議用繼承 std::exception 的類別main、執行緒函數、DLL 導出函數、WinMain、COM 方法必須捕獲所有異常,避免跨邊界傳播(C++ 異常不跨越 C 語言/OS 邊界)catch 時配合 boost::stacktrace 或平台 API 記錄呼叫堆疊exit() 的舊 C/C++ 代碼庫現代化throw 替換 exit() 是零侵入式將 C 風格錯誤處理升級為 C++ 異常機制的最佳捷徑。它保留 RAII 資源安全、賦予中間層恢復決策權、讓頂層統一制定退出策略,是整理燒餅代碼的利器。