標準 C++ 異常不攜帶堆棧跟蹤信息,捕獲時無法知曉拋出位置,嚴重阻礙調試與日誌分析。此外,硬件信號(如 SIGSEGV、SIGFPE)默認終止進程,無法進入 C++ 異常流程。
利用異常對象構造時機,調用平台 API 獲取調用棧:
#include <execinfo.h> // Linux
#include <exception>
#include <string>
#include <vector>
class TracedException : public std::exception {
std::vector<std::string> stackTrace_;
public:
TracedException() {
void* buffer[64];
int nptrs = backtrace(buffer, 64);
char** strings = backtrace_symbols(buffer, nptrs);
for (int i = 0; i < nptrs; ++i)
stackTrace_.emplace_back(strings[i]);
free(strings);
}
const char* what() const noexcept override { return "TracedException"; }
const auto& getStackTrace() const { return stackTrace_; }
};
Windows 下使用 CaptureStackBackTrace 或 StackWalk64 實現同樣效果。
設置信號處理器,拋出對應異常類型:
#include <signal.h>
#include <stdexcept>
class SignalException : public std::runtime_error {
int signo_;
public:
SignalException(int sig) : std::runtime_error("Signal"), signo_(sig) {}
int signal() const { return signo_; }
};
void signalHandler(int sig) {
throw SignalException(sig);
}
// 主程式啟動時註冊
signal(SIGSEGV, signalHandler);
signal(SIGFPE, signalHandler);
關鍵限制:信號處理器中只能調用 async-signal-safe 函數,拋出異常屬未定義行為(UB),但主流實現(GCC/Clang/MSVC)在 x86/x64 上均可工作。生產環境建議:信號處理器僅設置標誌,主循環輪詢拋出異常。
#include <exception>
#include <iostream>
void terminateHandler() {
try {
throw; // 重拋當前異常
} catch (const std::exception& e) {
std::cerr << "FATAL: " << e.what() << std::endl;
} catch (...) {
std::cerr << "FATAL: unknown exception" << std::endl;
}
std::abort(); // 或 exit(1)
}
int main() {
std::set_terminate(terminateHandler);
// ...
}
C++ 標準規定:全局/靜態對象構造/析構期間拋出異常導致 std::terminate。GCC/Clang 對此行為較寬容,但不可依賴。
解決方案:
std::expected (C++23) 或錯誤碼。main 之後。std::nothrow 配合錯誤碼檢查。std::exception,重寫 what()。catch (const MyEx& e))。std::lock_guard),保證異常安全。std::logic_error)與運行時錯誤(環境故障,如 std::runtime_error)。