当前位置: 首页 > news >正文

stdexcept介绍与使用指南

1. 概述

在 C++ 中,异常处理(Exception Handling)是一种重要的错误处理机制。当程序运行中遇到无法继续的错误时,可以通过 抛出异常(throw) 来中断当前流程,并让上层调用者决定如何处理。
C++ 标准库 <stdexcept> 定义了一组常用的异常类,这些类位于 std 命名空间中,方便我们在需要抛出常见类型的异常时直接使用,而无需自己定义异常类型。

头文件位置

#include <stdexcept>

2. <stdexcept> 中常用的异常类

类名继承关系用途说明
std::exception最顶层基类(定义在 <exception> 中)所有标准异常的基类
std::logic_error继承自 std::exception表示逻辑错误,通常是程序员的错误,运行前应能避免
std::runtime_error继承自 std::exception表示运行时错误,通常是外部环境因素造成
std::invalid_argument继承自 std::logic_error参数无效
std::domain_error继承自 std::logic_error数学定义域错误
std::length_error继承自 std::logic_error长度超出允许范围
std::out_of_range继承自 std::logic_error越界访问
std::range_error继承自 std::runtime_error运行时范围错误
std::overflow_error继承自 std::runtime_error数值溢出
std::underflow_error继承自 std::runtime_error数值下溢

继承层级结构可以表示为:

std::exception├── std::logic_error│    ├── std::invalid_argument│    ├── std::domain_error│    ├── std::length_error│    └── std::out_of_range└── std::runtime_error├── std::range_error├── std::overflow_error└── std::underflow_error

3. 构造与用法

每种异常类的构造函数通常接受一个 std::stringconst char* 作为错误信息。例如:

throw std::invalid_argument("Parameter must not be negative");

4. 基本使用示例

#include <iostream>
#include <stdexcept>double safe_divide(double a, double b) {if (b == 0) {throw std::invalid_argument("Division by zero is not allowed");}return a / b;
}int main() {try {std::cout << safe_divide(10, 0) << "\n";}catch (const std::invalid_argument &e) {std::cerr << "Invalid argument: " << e.what() << "\n";}catch (const std::exception &e) { // 捕获其他标准异常std::cerr << "Error: " << e.what() << "\n";}return 0;
}

运行结果

Invalid argument: Division by zero is not allowed

5. 分类与选用原则

  • 逻辑错误(std::logic_error 及其派生类)
    通常在 程序逻辑错误 时抛出,例如:

    • 传入函数的参数无效(invalid_argument
    • 索引越界(out_of_range
    • 违反数学定义域(domain_error
    • 数据结构的长度超限(length_error
  • 运行时错误(std::runtime_error 及其派生类)
    通常在 外部不可控因素 造成问题时抛出,例如:

    • 文件读取失败
    • 内存不足
    • 数值溢出(overflow_error
    • 范围计算错误(range_error

6. 捕获异常的注意事项

  1. 总是按引用捕获异常对象,避免对象切割:

    catch (const std::out_of_range& e) { ... }
    
  2. 按派生类到基类的顺序捕获,因为捕获顺序是从上到下匹配的:

    try { ... }
    catch (const std::out_of_range& e) { ... }
    catch (const std::logic_error& e) { ... } // 父类
    catch (const std::exception& e) { ... }   // 最通用
    
  3. 适时重抛throw;)以让更高层来处理异常。


7. 自定义异常与 <stdexcept> 结合

如果标准异常不足以表达业务含义,可以从 <stdexcept> 的异常类派生自定义类型:

class MyAppError : public std::runtime_error {
public:explicit MyAppError(const std::string& msg): std::runtime_error("MyAppError: " + msg) {}
};

这样做的好处是既能保留标准异常的行为,又能在需要时用 catch (const MyAppError&) 精确捕获。


8. 总结

  • <stdexcept> 提供了一套常用的异常类,覆盖了大多数逻辑错误与运行时错误场景。
  • 充分使用这些现成的异常类,可提升错误信息的语义性与代码可读性。
  • 对于不符合现有分类的特殊错误,可以继承这些类来自定义异常。
  • 使用异常处理应当注意捕获顺序、按引用捕获,以及合适的错误分类。

第二个问题

根据我的了解,像是索引越界这种异常一般如果不走.at()的话,对于数组的直接越界访问是会直接程序终止而不会抛出异常的,引入会有什么不同吗,不是仍然需要手动抛出吗


你说得完全正确,C++ 里大部分容器或原生数组的越界访问,默认是不会自动抛出 <stdexcept> 异常的,这个问题很容易让初学者误解。
我可以详细解释一下为什么,以及 <stdexcept> 在这种场景下到底能做什么。


1. 原生数组的越界访问

假设你用的是 C 风格数组:

int arr[3] = {1, 2, 3};
arr[5] = 10;  // 越界写
  • 不会自动抛出异常
    原生数组在编译器或运行时没有边界检查机制,越界访问会导致未定义行为(Undefined Behavior)。
  • 未定义行为可能是
    • 修改了无关内存
    • 程序直接崩溃(Segmentation fault)
    • 看似“正常”地运行但隐藏 bug

<stdexcept> 并不会改变这一行为,因为它只是提供了异常类,并不自动插入边界检查逻辑。


2. std::vector.at() 的区别

在标准容器 std::vector 中:

#include <vector>std::vector<int> v = {1, 2, 3};
v[5] = 10;      // 未定义行为,不抛异常
v.at(5) = 10;   // 抛出 std::out_of_range 异常
  • operator[] 不进行边界检查(出于性能考虑)
  • .at() 会进行边界检查,如果越界就主动抛出 std::out_of_range(定义在 <stdexcept> 里)

这是 <stdexcept> 在标准容器中的一个真实应用场景:标准库内部用它来描述和传递容器操作的逻辑错误


3. 如果是你自己的数据结构

如果你实现了自己的数据结构,比如自定义动态数组,你需要手动在检测到越界时抛出 <stdexcept> 的异常

#include <stdexcept>class MyArray {
public:MyArray(size_t size) : size_(size), data_(new int[size]) {}~MyArray() { delete[] data_; }int& at(size_t index) {if (index >= size_) {throw std::out_of_range("Index out of range");}return data_[index];}private:size_t size_;int* data_;
};

这里的 throw std::out_of_range(...) 就是你手动利用 <stdexcept> 提供的异常类。


4. <stdexcept> 究竟改变了什么?

  • 会自动让原生数组或 operator[] 越界时抛异常
  • 它的作用是提供统一的异常类接口和层次结构
    • 这些类在标准容器实现和你自己的代码里都可以复用
    • 比如 .at() 的实现内部就是用 throw std::out_of_range(...)
  • 如果你想让某个 API 在出现逻辑或运行时错误时抛出统一风格的错误,去 <stdexcept> 里找对应的异常类更规范,更可读

5. 总结

  • 引入 <stdexcept> 不会改变原本的越界行为,C 风格数组和不做检查的容器操作依然会直接触发 UB(可能崩溃)。
  • <stdexcept> 是用来定义异常类型的库,是否抛出这些异常完全由代码逻辑(你或标准容器的实现)决定。
  • 如果你希望在越界时得到可控的错误处理,就需要:
    • 使用容器的 .at() 方法(内部自动抛出异常)
    • 或者自己手动写边界检查并 throw 对应的 <stdexcept> 异常。
http://www.dtcms.com/a/360899.html

相关文章:

  • 13 选 list 还是 vector?C++ STL list 扩容 / 迭代器失效问题 + 模拟实现,对比后再做选择
  • 基于 HTML、CSS 和 JavaScript 的智能图像边缘检测系统
  • 【数据分享】上市公司-信息透明度综合指数数据(2003-2023)
  • Neurokit———开源多模态电生理数据处理Python包
  • ubantu安装配置hive
  • Java内功修炼(3)——并发的四重境界:单例之固、生产消费之衡、定时之准、池化之效
  • 响应式编程框架Reactor【9】
  • 《论文阅读》从心到词:通过综合比喻语言和语义上下文信号产生同理心反应 2025 ACL findings
  • 【HTML】draggable 属性:解锁网页交互新维度
  • SpringAI模型评估
  • python爬虫之requests库的使用(小白五分钟从入门到精通)
  • Selenium 自动化测试实战:绕过登录直接获取 Cookie
  • 如何用AI视频增强清晰度软件解决画质模糊问题
  • 血缘元数据采集开放标准:OpenLineage Guides 使用 Apache Airflow® 和 OpenLineage + Marquez 入门
  • IPC 进程间通信 interprocess communicate
  • 【macOS】垃圾箱中文件无法清理的“含特殊字符文件名”的方法
  • 应用平台更新:可定制目录、基于Git的密钥管理与K8s项目自动化管理
  • Python 爬虫基础教学
  • C#/.NET/.NET Core技术前沿周刊 | 第 52 期(2025年8.25-8.31)
  • C++ 面试高频考点 力扣 35. 搜索插入位置 二分查找 左右端点查找 题解 每日一题
  • RocksDB 在 macOS M 系列 上运行时报错的解决方案
  • 【公告】更新预告
  • vite基础讲解
  • 超越Transformer:语言模型未来的认知革命与架构重构
  • Golang之GoWorld深度解析:基于Go语言的分布式游戏服务器框架
  • 新启航技术白皮书:激光频率梳如何实现 130mm 深孔 2μm 级无遮挡 3D 轮廓测量
  • OpenCV-Python Tutorial : A Candy from Official Main Page(五)
  • 使用Spring Boot对接印度股票市场API开发实践
  • Burp Suite 插件 | 提供强大的框架自动化安全扫描功能。目前支持1000+POC、支持动态加载POC、指定框架扫描。
  • 一体化运维平台自动化模块:3 大场景解放运维双手