set_terminate 只捕获未被任何 catch 块处理的异常,包括类型不匹配、析构函数抛异常、throw() 规范违反等;它不捕获已进入 catch 后新抛出且未处理的异常,也不处理系统级错误(如 Windows 访问违例)。

set_terminate 函数能捕获哪些异常
它只捕获「未被任何 catch 块处理」的异常,包括:抛出后找不到匹配 catch 的类型、析构函数里意外抛出异常(C++11 起默认为 noexcept)、以及 throw() 异常规范被违反的情况。它不捕获已进入 catch 但后续又抛出的新异常(除非那个新异常也没被处理)。
常见错误现象:terminate called after throwing an instance of 'std::runtime_error' 这类崩溃信息出现前,其实 set_terminate 已经被触发过了——只是默认行为直接调用 std::abort,没给你留打印机会。
- 必须在程序启动早期调用(比如
main()开头),否则可能错过首次异常 - 自定义处理函数不能返回,也不能再抛异常,否则会再次触发
terminate,形成死循环 - 若使用多线程,
set_terminate是进程级设置,对所有线程生效
怎么写一个安全可用的 terminate handler
核心是「只做有限、确定的事」:记录日志、打印堆栈(如果环境支持)、快速退出。避免调用 malloc、std::string 构造、IO 流操作等可能依赖异常机制或分配内存的代码。
示例中用 write() 系统调用而非 std::cout,因为前者不抛异常、不分配内存:
void my_terminate() {
const char msg[] = "FATAL: uncaught exception\n";
write(2, msg, sizeof(msg) - 1); // stderr
_exit(1); // 不要 exit(),它会跑 dtor,可能再抛异常
}
- 用
_exit()而非exit()或return,跳过所有对象析构 - 避免调用
std::current_exception()—— 它在terminatehandler 中行为未定义 - 若需堆栈信息,优先用
backtrace()+backtrace_symbols_fd()(Linux),它们是异步信号安全的
为什么 try-catch(...) 在 main 里不够用
它只能兜住 main 函数体内的异常,但捕获不到:全局/静态对象构造时抛出的异常、动态库加载时的初始化异常、线程入口函数中未捕获的异常(除非你每个线程都手动包一层)。
-
main的try块无法覆盖静态对象的构造函数调用 - 线程中抛异常后若没
catch,会直接调用std::terminate,不经过主线程的try - 某些平台(如 Windows SEH 混合场景)下,结构化异常(如访问违规)也不会被
catch(...)捕获
Windows 上要注意的特殊点
MSVC 默认启用 /EHsc,此时 C++ 异常和 SEH 是分离的。set_terminate 对访问违例(0xC0000005)、除零等系统级错误完全无效。
- 真要捕获崩溃,得用
SetUnhandledExceptionFilter或AddVectoredExceptionHandler -
set_terminate和SetUnhandledExceptionFilter是两套机制,互不替代 - 混用 C++ 异常和 SEH 时,务必确认编译器异常处理模型(
/EHsvs/EHa),后者会让catch(...)捕获 SEH,但代价是性能下降且不标准
实际项目里,set_terminate 是最后一道防线,不是异常处理主干。真正该花力气的是让关键路径有明确 catch、静态对象构造尽量无副作用、线程入口加防护——set_terminate 只负责别让崩溃悄无声息。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











