throw语句抛出的是表达式结果对象,推荐用std::runtime_error等标准异常类而非裸字符串或整数,因后者无类型信息且易导致捕获不匹配;异常对象必须可拷贝,不可抛局部地址或new指针。

throw 语句只能抛出一个表达式,不能直接抛字符串字面量(在 C++11 及以后)
很多人写 throw "error" 能编译通过,但实际抛出的是 const char* 类型指针,不是 std::string 或 std::runtime_error。这种写法容易导致捕获端类型不匹配,或忘记 delete[](虽然字面量不用释放,但语义混乱)。推荐统一用标准异常类:
-
throw std::runtime_error("file not found")—— 最常用,构造时自动拷贝消息 -
throw std::logic_error("invalid argument")—— 用于程序逻辑错误(如参数非法) -
throw std::out_of_range("index out of bounds")—— 容器越界等场景更语义明确
throw 表达式必须位于函数体内,且不能出现在 noexcept 函数中(除非抛出的是空异常)
如果函数声明了 noexcept(包括隐式 noexcept(true) 的析构函数、移动操作符),而你写了 throw,编译器会报错;若运行时抛出,程序直接调用 std::terminate()。常见踩坑点:
- 在
~MyClass()析构函数里写throw—— C++11 起默认是noexcept,必须显式写成~MyClass() noexcept(false) - 重载
operator=并标记为noexcept,但内部又调用了可能抛异常的函数 —— 编译不过 - 模板函数推导出
noexcept属性后,意外禁用了throw
throw 后面的表达式类型决定 catch 能否匹配,注意切片和引用绑定
比如 throw Derived();,如果 catch (Base b)(值捕获),会发生对象切片;而 catch (const Base& b) 才能安全访问完整派生状态。更关键的是:抛出临时对象时,catch 中的引用绑定是允许的(C++ 标准允许延长其生命周期),但 catch 块退出后对象即销毁。
- 避免
catch (std::exception e)—— 值传递引发拷贝,且丢失派生类信息 - 优先写
catch (const std::exception& e)—— 零拷贝、保类型、可调e.what() - 如果抛的是
throw MyException{42, "bad"};,确保catch (const MyException&)在catch (const std::exception&)之前,否则会被基类捕获器提前截获
不要用 throw 抛原始指针或局部数组地址
像 int x = 42; throw &x; 或 throw arr;(局部数组名)会导致悬垂指针 —— throw 进入栈展开阶段后,原栈帧已被销毁,捕获端解引用就是未定义行为。同样,throw new MyError 会造成内存泄漏(没人负责 delete)。
立即学习“C++免费学习笔记(深入)”;
- 绝对不要
throw new std::runtime_error(...) - 也不要
throw std::unique_ptr<:exception>(...)</:exception>—— 异常对象需满足可复制/可移动,智能指针反而增加间接层和风险 - 所有异常对象应是自动存储期的临时对象,由异常机制内部管理生命周期
noexcept,哪些层级负责清理资源,以及 catch 块是否真正处理了错误还是只做了日志就吞掉。最常被忽略的是析构函数里的异常约束和多级 catch 的顺序。


















