catch(...)必须放在所有catch分支之后,否则会拦截更具体的异常;它能捕获任意类型异常但不提供异常对象信息,也无法直接重抛原异常。

用 catch(...) 捕获所有异常,但必须放在最后
想一写就兜住所有异常?catch(...) 是 C++ 唯一能匹配任意类型(包括非 std::exception 派生类、内置类型、甚至 nullptr)的语法。但它不提供异常对象信息,也不能重抛原异常(除非你手动保存)。
常见错误是把它写在其他 catch 分支前面,导致更具体的捕获被跳过:
try {
throw std::runtime_error("boom");
} catch(...) { // ❌ 错了:这个先匹配,下面永远进不去
std::cout
-
catch(...)必须是catch链的最后一个分支 - 它不能绑定异常对象,所以无法访问
.what()、.code()等成员 - 如果后续还要处理或日志,得在
catch(...)里用std::current_exception()获取std::exception_ptr
想记录异常内容?必须配合 std::current_exception() 和 std::rethrow_exception()
单纯 catch(...) 只能知道“有异常”,但不知道是什么。要获取可打印的描述,得靠标准库提供的异常指针机制:
try {
throw std::logic_error("invalid arg");
} catch(...) {
auto ptr = std::current_exception(); // 拿到当前异常的句柄
try {
if (ptr) std::rethrow_exception(ptr); // 重新抛出,再捕获具体类型
} catch(const std::exception& e) {
std::cerr
-
std::current_exception()只在catch块内有效,离开即失效 - 重抛后再次
catch是唯一能安全拿到原始异常信息的方式 - 注意嵌套
try的开销:不是零成本,频繁使用会影响性能
为什么不用 catch(...) 替代所有具体捕获?
因为丢失类型信息会掩盖问题本质。比如 std::bad_alloc 和 std::out_of_range 处理策略完全不同:
-
std::bad_alloc往往需要立即释放资源、降级或退出,不能简单忽略 -
std::out_of_range可能只是输入校验疏漏,适合返回错误码或重试 - 捕获
int或char*异常(老式 C 风格)说明代码混合了新旧异常模型,应逐步清理 - 某些编译器(如 MSVC)在启用
/EHsc时,默认不捕获结构化异常(SEH),catch(...)也无能为力
真正“全局”捕获?靠 std::set_terminate() 和信号处理
catch(...) 只管 try 块内的异常。未被捕获的异常会调用 std::terminate() —— 这才是程序崩溃前的最后一道钩子:
void my_terminate() {
auto ptr = std::current_exception();
if (ptr) {
// 同上,用 rethrow_exception 提取信息
}
std::abort(); // 或写 minidump、log backtrace
}
std::set_terminate(my_terminate);
-
std::set_terminate设置的是未捕获异常的最终处理器,不是“捕获”而是“兜底善后” - 它不能恢复执行,只能记录、清理、终止
- Unix 上还需处理
SIGSEGV等信号(需sigaction),这和 C++ 异常无关,别混淆
真正难的不是语法,是区分哪些异常该当场处理、哪些该传播、哪些必须终止进程——catch(...) 容易写,但滥用会让错误定位变困难。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











