标准异常类均继承自std::exception,但应按错误性质分层捕获具体子类:logic_error用于可预防的编程错误(如invalid_argument),runtime_error用于不可预测的运行时失败(如system_error),bad_alloc直接继承exception;捕获须从具体到宽泛,且what()返回的字符串需立即拷贝。

标准异常类都继承自 std::exception,但直接用它捕获往往不够精准——你真正需要的,是按错误性质分层选对具体子类,否则容易漏掉关键语义或掩盖真实问题。
std::exception 是所有标准异常的唯一公共基类
它只提供虚析构函数和 what() 成员函数,不带任何业务含义。不能靠它区分“参数错”还是“内存不足”,仅适合兜底捕获。
-
std::exception本身不可直接实例化(纯虚析构),必须用其子类 - 所有标准异常定义在
(除std::bad_alloc在、std::bad_cast在等少数例外) - 继承结构不是扁平的:
std::logic_error和std::runtime_error是两个并列的一级派生类,不是同级兄弟
std::logic_error 适用于可预防的编程错误
这类异常表示逻辑上不该发生的错误,通常源于调用方传参不当或违反函数契约,比如把负数传给只接受正数的构造函数。
- 常见子类:
std::invalid_argument(通用参数错误)、std::out_of_range(如vector::at()越界)、std::domain_error(数学定义域违规,如对负数开根) - 它们都继承自
std::logic_error,而后者又继承自std::exception - 注意:
std::length_error表示容器长度超限(如std::string尝试扩容到超过max_size()),不是字符串内容长度检查
std::runtime_error 用于不可预测的运行时失败
这类异常无法在编译期或调用前静态判断,往往依赖外部状态,比如磁盘满、网络断开、浮点溢出等。
- 常见子类:
std::system_error(封装errno或 Windows 错误码)、std::ios_base::failure(I/O 流操作失败)、std::overflow_error(算术上溢,非整数除零) -
std::bad_alloc是特例:它不继承自std::runtime_error,而是直接继承std::exception,因为内存分配失败属于底层资源问题,语义层级更高 - 不要用
std::runtime_error直接抛出——它没有构造函数接收错误码,应优先选更具体的子类
catch 顺序和类型匹配容易踩坑
捕获顺序必须从具体到宽泛,否则宽泛的 catch(std::exception& e) 会提前截断更具体的异常类型。
- 错误写法:
catch(std::exception& e)放在catch(std::out_of_range& e)前面 → 后者永远执行不到 - 推荐写法:先捕获
std::out_of_range、std::system_error等具体类型,最后用catch(const std::exception& e)兜底 - 避免裸指针捕获:
catch(std::exception* e)是危险的,标准库抛出的是对象,不是指针 -
catch(...)可捕获非标准异常(如int或char*),但它无法访问what(),应仅作日志记录后重新抛出
最常被忽略的一点是:标准异常类的构造函数大多接受 const char* 或 std::string,但 what() 返回的 C 字符串生命周期绑定在异常对象本身——如果在 catch 块外保存该指针,行为未定义。需要持久化描述时,应立即拷贝为 std::string。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











