首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ 異常處理進階:堆棧跟蹤、信號轉換與全局捕獲實戰

     編輯:C++ 入門

異常處理的核心痛點

標準 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 實現同樣效果。

方案二:信號轉 C++ 異常

設置信號處理器,拋出對應異常類型:


#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 配合錯誤碼檢查。

現代 C++ 異常最佳實踐

  1. 異常類繼承 std::exception,重寫 what()。
  2. 按值拋出,按引用捕獲(catch (const MyEx& e))。
  3. 資源管理用 RAII(智能指針、std::lock_guard),保證異常安全。
  4. 區分邏輯錯誤(precondition violation,如 std::logic_error)與運行時錯誤(環境故障,如 std::runtime_error)。
  5. 異常不用於控制流,僅用於「無法在局部處理」的錯誤。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved