应使用 std::set_terminate 注册自定义终止处理器,在其中打印调用栈、线程 ID 和时间戳;std::current_exception() 在 terminate handler 中通常失效,因异常已处于未捕获状态且栈开始解退。

如何设置全局未捕获异常处理器
在 C++ 程序崩溃前,系统会调用 std::set_terminate 注册的终止处理函数;而真正能“捕获并打印”所有未处理异常的,是 std::set_unexpected(C++17 起已弃用)和更可靠的 std::set_terminate 配合自定义 terminate handler。但注意:它不是“捕获”,而是拦截异常逃逸出 main 后的最终出口——此时异常对象仍可访问,但栈已开始解退,无法再用 catch 捕获。
关键点:你不能用 try/catch 包裹整个 main 来“捕获所有异常”,因为线程、静态初始化、atexit 回调等场景抛出的异常无法被该 try 块覆盖。
- 必须在程序启动早期(如 main 开头)调用
std::set_terminate - 自定义 terminate handler 中,
std::current_exception()通常返回空std::exception_ptr(因栈已 unwind),所以不能依赖它获取异常类型或 what() - 推荐做法:在 terminate handler 中直接打印调用栈(需第三方库如
backtrace或boost::stacktrace),并记录当前线程 ID 和时间戳
为什么 std::current_exception() 在 terminate handler 中大多失效
std::current_exception() 只在异常处于 active 状态时有效(即仍在栈上、尚未进入 unwind 阶段)。而 std::terminate 被调用时,异常已被系统标记为“未被捕获”,标准明确要求此时栈开始强制解退,std::current_exception() 行为是未定义或返回空指针。
- 实测 GCC/Clang 下,
std::current_exception()在 terminate handler 中几乎总是返回空std::exception_ptr - 不要写类似
if (auto e = std::current_exception()) { ... }并期待能拿到what() - 若真需要异常信息,应在可能抛出的位置加日志,或使用 RAII guard +
try/catch显式捕获后记录
跨平台打印异常与堆栈的最小可行方案
Linux/macOS 下可用 backtrace + abi::__cxa_demangle 打印符号化堆栈;Windows 下需 StackWalk64 或 CaptureStackBackTrace。但最简且可移植的做法是:只打印异常类型名(靠 RTTI)+ 当前线程 ID + 时间戳,并配合编译期开启调试信息(-g)和禁用优化(-O0)便于后续 gdb 分析。
立即学习“C++免费学习笔记(深入)”;
示例 terminate handler:
void my_terminate() {
std::cerr << "[FATAL] Unhandled exception at "
<< std::chrono::system_clock::now().time_since_epoch().count()
<< " in thread " << std::this_thread::get_id() << "\n";
// 不要调用 std::rethrow_exception(std::current_exception())
std::abort(); // 或 _Exit(1) 避免再次触发 dtor
}
- 避免在 terminate handler 中抛出新异常(会直接调用
std::abort) - 不要调用
std::exit或任何可能触发全局对象析构的函数 - 优先用
_Exit(1)(POSIX)或std::quick_exit(1)(C++11)确保立即终止
多线程环境下异常捕获的现实限制
主线程的未捕获异常可通过 std::set_terminate 拦截,但其他线程中未捕获的异常默认直接终止整个进程(C++ 标准规定),且不会经过主线程的 terminate handler——除非你显式为每个线程设置自己的异常处理逻辑。
- std::thread 构造函数包装器中必须手动 try/catch,例如:
std::thread([]{ try { f(); } catch(...) { log_and_abort(); } }) - std::async、std::jthread(C++20)同样不自动传播异常到主线程之外;未 get() 的 future 异常会在 ~future 时调用 terminate
- 第三方线程池(如 tbb、folly)通常提供异常回调机制,需查阅其文档配置
真正“捕获所有”只是一种错觉——你能做的,是在关键路径加防护,在进程级入口设兜底,然后靠日志、core dump 和 symbolicated crash report 定位问题。异常逃逸本身已是严重缺陷,不该指望运行时补救。



















