c++要求catch必须声明异常类型,catch{}非法;常用catch(const std::exception& e)或catch(...);析构函数中禁止throw,否则触发std::terminate();noexcept函数内抛异常亦会终止程序;异常对象生命周期限于catch块内。

catch 里不写类型会编译失败
直接写 catch {} 是非法的,C++ 要求每个 catch 必须声明能捕获的异常类型。最常见的是 catch (const std::exception& e),它能兜住绝大多数标准异常;如果想捕获所有(包括 int、char* 这类非对象异常),得显式写 catch (...) —— 但它不提供异常对象,没法调用 e.what()。
容易踩的坑:
-
catch (std::exception e)(传值)会触发拷贝,可能抛新异常或性能差,必须用const std::exception& -
catch (std::runtime_error e)会漏掉std::logic_error及其子类,除非你明确只关心这一种 -
catch (...)后没法知道是什么错,仅适合做日志记录+重新抛出:throw;(注意不是throw e;)
析构函数里 throw 会导致程序终止
C++ 规定:如果在栈展开过程中(也就是正在执行 catch 或析构函数时)又抛出异常,且当前没有更外层的 catch 捕获它,std::terminate() 会被立即调用 —— 进程直接退出,不跑 atexit、不刷缓冲区。
所以:
立即学习“C++免费学习笔记(深入)”;
- 绝不要在析构函数里写
throw,哪怕你“觉得这里不该出错” - 如果析构中可能失败(比如关文件句柄失败),改用返回错误码、记日志、或设标志位,别抛异常
-
noexcept默认修饰析构函数(C++11 起),显式写~MyClass() noexcept更安全,编译器会帮你拦住误 throw
throw 表达式和 noexcept 不匹配会崩
函数声明了 noexcept,但内部调用了可能抛异常的代码(比如没包 try/catch 的 std::string::at()),运行时一旦真抛了,就调 std::terminate()。
典型场景:
- 自己写的工具函数标了
noexcept,但里面用了new(可能抛std::bad_alloc) - 调第三方库函数前没查文档,对方没声明
noexcept,你却假设它不会抛 -
std::vector::operator[]不抛异常,但at()会 —— 选错接口就破功
建议:不确定是否抛异常的函数,别轻易标 noexcept;真要标,先包一层 try/catch 把异常吞掉或转成错误码。
异常对象生命周期容易被误解
抛出的异常对象是临时的,存储在特殊内存区,生命周期从 throw 开始,到对应 catch 块结束为止。这意味着:
-
catch块里取的引用(如const std::exception& e)是安全的,指向这个临时对象 - 但千万别把该引用存成成员变量或返回给调用方 ——
catch一退出,对象就销毁,变成悬垂引用 - 如果需要长期持有异常信息,得拷贝内容:比如
std::string msg = e.what();
复杂点在于:有些编译器对异常对象做 RVO/NRVO 优化,但语义上你不能依赖这点 —— 生命周期规则才是唯一可靠依据。










