throw必须抛出对象而非裸指针或字面量,如throw "error"或throw nullptr会调用std::terminate();catch需按派生类到基类顺序书写,否则基类catch会截获所有派生异常。

throw 抛出的必须是对象,不能是裸指针或字面量
很多初学者写 throw "error" 或 throw nullptr,这会导致程序调用 std::terminate() 直接终止。C++ 要求 throw 表达式必须抛出一个可复制(或移动)的完整对象,比如 std::string、std::runtime_error,或者自定义的异常类。
-
throw std::runtime_error("file not found")✅ 安全且带上下文 -
throw 42❌ 不推荐:整数不是语义清晰的异常类型,且无法携带消息 -
throw new std::exception()❌ 危险:抛出的是指针,catch需手动delete,极易内存泄漏
catch 块顺序必须从派生类到基类
如果多个 catch 块并存,编译器按书写顺序匹配第一个能接受的类型。若把 catch (const std::exception& e) 写在最前,它会捕获所有派生异常(如 std::logic_error),导致后续更具体的 catch 永远不会执行。
try {
throw std::out_of_range("index too large");
} catch (const std::out_of_range& e) { // ✅ 必须放前面
std::cerr << "range error: " << e.what() << '\n';
} catch (const std::logic_error& e) { // ✅ 次之
std::cerr << "logic error: " << e.what() << '\n';
} catch (const std::exception& e) { // ✅ 最后兜底
std::cerr << "other std exception: " << e.what() << '\n';
}noexcept 说明符影响异常传播路径
函数声明为 noexcept 后,若内部抛出异常且未被本函数捕获,会立即调用 std::terminate(),而不是向上层传播。这不是“禁止抛出”,而是“承诺不向外传递异常”。
-
void foo() noexcept { throw std::runtime_error("oops"); }→ 程序直接终止 -
void bar() noexcept { try { throw ...; } catch (...) { /* handled */ } }✅ 合法:异常在函数内被吞掉 - 析构函数默认是
noexcept,因此绝不要在析构函数里抛异常(C++11 起标准禁止)
std::current_exception() 用于跨线程或延迟重抛
普通 throw; 只能在当前 catch 块中重抛当前异常。若想保存异常、跨作用域甚至跨线程再抛出,必须用 std::current_exception() 获取 std::exception_ptr。
立即学习“C++免费学习笔记(深入)”;
std::exception_ptr saved_exc;
try {
risky_operation();
} catch (...) {
saved_exc = std::current_exception(); // 保存异常对象副本
}
// 在另一段代码中(比如线程回调里)
if (saved_exc) {
std::rethrow_exception(saved_exc); // 重新抛出原异常,栈信息保留
}
注意:std::exception_ptr 是引用计数的,拷贝安全;但原始异常对象生命周期由其首次抛出处决定,std::current_exception() 会做深拷贝,所以不必担心悬空。
真正容易被忽略的是:异常对象的拷贝构造函数不能抛异常(否则 std::current_exception() 失败时也会 terminate),所以自定义异常类的拷贝构造应声明为 noexcept。











