C++ 標準庫提供了完善的異常處理機制(try/catch/throw),但在某些場景下(如整合 C 庫、跨語言呼叫),開發者仍可能被迫使用 C 風格的 setjmp/longjmp。然而,這對組合在 C++ 中極難「和睦相處」,核心原因在於物件生命週期管理機制的根本差異。
觀察以下兩段程式碼的執行結果差異:
setjmp 後建立void main() {
jmp_buf mark;
int jmpret = setjmp(mark);
if (jmpret == 0) {
MyTest myobj; // 建立物件
test(); // 內部呼叫 longjmp
} else {
printf("捕獲到異常\n");
}
}
輸出:
構造一個 MyTest 類型的對象 析構銷毀一個 MyTest 類型的對象 捕獲到一個異常
看似正常,析構函數被正確呼叫。
longjmp 所在函數內建立void test1() {
MyTest myobj; // 位置稍微移動
longjmp(mark, 1);
}
輸出:
構造一個 MyTest 類型的對象 捕獲到一個異常
析構函數未被呼叫! 物件資源洩漏,若涉及鎖、檔案控制代碼等,將導致死鎖或系統崩潰。
setjmp 與 longjmp 之間、且編譯器能追蹤生命週期的作用域內建構的物件,才可能(視編譯器實現)獲得析構機會。跨函數、動態分配、或編譯器優化後,析構極大概率缺失。try/catch),確保 RAII 生效。setjmp 點之前完成所有 C++ 物件建構,避免在 longjmp 跳轉路徑上有活躍的 C++ 物件。std::unique_ptr 或 std::shared_ptr 管理資源,並在 longjmp 目標點手動清理(極不推薦,易出錯)。setjmp/longjmp,對外拋出 C++ 異常。-fnon-call-exceptions 等選項嘗試支援,但不可移植,生產環境慎用。結論:setjmp/longjmp 在 C++ 中屬於「核武器級」危險操作,除非極度確定無 C++ 物件生命週期介入,否則堅決避免使用。