throw; 是C++中唯一安全的重新抛出机制,保持原始异常身份与类型;不可在noexcept函数或析构函数中使用,否则调用std::terminate();需用std::current_exception()跨线程延迟重抛。

throw 后面不加表达式就是重新抛出当前异常
在 catch 块里想把刚捕获的异常原样传给上层,直接写 throw; 就行——注意后面没有表达式,也不能带分号以外的任何东西。这不是语法糖,是 C++ 明确规定的“重新抛出”(rethrow)机制。
常见错误现象:throw e; 看起来很自然,但会触发拷贝构造,可能切片、抛新对象、丢失原始类型信息;更糟的是,如果 e 是临时对象或已析构,行为未定义。
-
throw;保持原始异常对象身份、类型、栈展开状态,是唯一安全的“原样上传”方式 - 只能在活跃的
catch块中用,函数外或try块里单独写throw;会调用std::terminate() - 如果当前
catch捕的是基类引用,throw;仍能准确还原派生类类型(RTTI 支持)
重新抛出前修改异常内容必须用 throw 表达式
想加日志、包装错误、转成另一种类型,就不能用 throw;,得显式构造新异常并 throw 出去。这时候你是在抛新异常,不是重抛。
使用场景:统一错误码封装、补充上下文、适配不同模块的异常约定。
立即学习“C++免费学习笔记(深入)”;
- 例如把
std::runtime_error包进自定义MyAppError:throw MyAppError("DB failed: " + e.what(), e.code()); - 避免裸
throw std::string(...)或throw "C-string":它们不是从std::exception派生,上层catch (const std::exception&)捕不到 - 性能影响:每次
throw表达式都会触发栈展开 + 异常对象构造 + 复制/移动,比throw;开销大得多
noexcept 函数里 throw; 会直接 terminate
如果当前函数声明了 noexcept(包括隐式 noexcept(true)),而你在它的 catch 块里写了 throw;,程序立即调用 std::terminate(),不会继续向上找 catch。
原因:编译器认为该函数承诺“绝不会让异常逃逸”,而 throw; 违反了这个契约。
- 检查调用链:确认从
noexcept函数到最外层main之间,所有中间函数是否都标记了noexcept - 兼容性陷阱:C++11 默认析构函数是
noexcept(true),所以在析构函数的catch里绝对不要写throw; - 调试技巧:开启编译器警告(如 GCC 的
-Wnoexcept)能提前发现这类冲突
std::current_exception() 适合跨线程或延迟重抛
当不能立刻 throw;(比如要存起来、发给另一个线程、或者等某个条件满足后再抛),就得用 std::current_exception() 把异常对象捕获为 std::exception_ptr。
它不复制异常对象,而是共享引用计数,支持跨作用域、跨线程安全传递。
- 保存:
auto ep = std::current_exception();(必须在catch块内) - 重抛:在任意有
catch的上下文中调用std::rethrow_exception(ep); - 注意:如果原始异常对象已销毁(比如
catch块退出),ep仍有效;但若原始异常是局部对象且没被拷贝,std::rethrow_exception可能抛std::bad_exception
noexcept 和析构函数里的取舍,一不留神就静默崩溃。










