最简可用结构为:try { 可能抛异常的代码 } catch (const std::exception& e) { 处理异常 }; 未捕获的异常将沿调用栈传播,最终触发std::terminate终止程序。

try catch 基本写法长什么样
直接上最简可用结构:try 块包住可能抛异常的代码,catch 块接住对应类型的异常。C++ 不强制要求捕获所有异常,但没被 catch 的异常会一路向上抛,最终调用 std::terminate 终止程序。
常见写法:
try {
risky_function(); // 可能 throw std::runtime_error 或其他
} catch (const std::exception& e) {
std::cerr
-
catch参数必须是引用或值类型,推荐用const std::exception&避免切片和拷贝开销 -
catch(...)是兜底,但无法获取异常信息,慎用;别把它放在第一个,否则后面所有catch都不会执行 - 不要在
catch块里漏掉throw;(空抛)或throw e;(重抛),前者保留原异常栈信息,后者会生成新对象、丢失原始位置
throw 什么才能被 catch 到
C++ 允许 throw 任意类型:内置类型(int、const char*)、类对象、甚至 nullptr。但只有能被 catch 参数匹配上的才会被捕获。
- 抛
std::string,不能用catch(std::exception&)捕获(std::string不继承自std::exception) - 抛
MyError()(自定义类),若未继承std::exception,就只能靠具体类型或catch(...)接 - 推荐统一用标准异常派生类:
std::runtime_error、std::invalid_argument等,方便上层统一处理 - 函数声明要不要加
noexcept?如果明确不抛异常,加上它可让编译器优化,且一旦违反会直接调用std::terminate—— 这比静默失败更安全
常见崩溃场景和避坑点
很多“程序崩了没进 catch”不是语法错,而是环境或逻辑问题。
- 在构造函数初始化列表里抛异常?可以,但对象没完成构造,析构函数不会执行 —— 资源得靠 RAII 自动清理,别指望手动释放
- 在
catch块里又抛新异常,而当前函数没声明noexcept(false)(默认就是 false)?没问题;但如果外层没对应catch,照样 terminate - 多线程环境下,异常不能跨线程传播。一个线程里
throw,另一个线程的try永远抓不到 —— 得用std::promise/std::future或错误码传递 - 动态库(DLL/so)边界抛异常极危险:不同编译器/标准库对异常对象布局可能不兼容,容易内存损坏。跨模块优先用返回错误码 +
std::error_code
要不要在每个函数都套 try catch
不需要,也不应该。异常是“异常”情况的信号,不是控制流工具。
- 在 main() 顶层加一层
try/catch(...)是合理兜底,至少避免黑屏退出 - 在关键资源操作后(如文件写入、网络发送)做针对性捕获,比如
catch(const std::filesystem::filesystem_error&),再决定重试还是降级 - 别为了“看起来健壮”而在循环里每个迭代都包
try—— 如果异常高频发生,说明设计有问题,该修逻辑或换策略 - 注意性能:没抛异常时,
try块几乎零开销(现代编译器用表驱动实现);但频繁抛/捕获确实有代价,尤其是栈展开过程
真正难的从来不是语法怎么写,而是判断哪条路径算“异常”、哪个层级该负责恢复、以及跨模块时敢不敢信对方抛出来的类型。这些没法靠 try/catch 自动解决。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











