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

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.xdnf.cn/news/19773.html

相关文章:

  • 【LeetCode】3670. 没有公共位的整数最大乘积 (SOSDP)
  • Day19_【机器学习—线性回归 (3)—回归模型评估方法】
  • Docker一键快速部署压测工具,高效测试 API 接口性能
  • ES6手录01-let与const
  • 学习日记-spring-day47-9.1
  • PyCharm 2025版本中新建python工程文件自动创建.venv的意义和作用
  • 教育 AI 的下半场:个性化学习路径生成背后,技术如何平衡效率与教育本质?
  • 第二十八天-DAC数模转换实验
  • “便农惠农”智慧社区系统(代码+数据库+LW)
  • 【深度学习基础】深度学习中的早停法:从理论到实践的全面解析
  • OpenCV C++ 入门实战:从基础操作到类封装全解析
  • UART控制器——ZYNQ学习笔记14
  • QT中的HTTP
  • GSM8K 原理全解析:从数学推理基准到大模型对齐的试金石
  • 五、练习2:Git分支操作
  • 安卓版 Pad 搭载 OCR 证件识别:酒店入住登记的高效解法
  • 永磁同步电机无速度算法--高频脉振方波注入法(新型位置跟踪策略)
  • Meteor主题友链页面自研
  • QT中的TCP
  • HTML应用指南:利用GET请求获取全国招商银行网点位置信息
  • IS-IS的原理
  • MySQL 性能调优与 SQL 优化的核心利器
  • Windows 命令行:cd 命令1,cd 命令的简单使用
  • 【软件开发工程师の校招秘籍】
  • 安装nodejs安装node.js安装教程(Windows Linux)
  • 盲盒抽谷机小程序开发:如何用3D技术重构沉浸式体验?
  • 闭包的简单讲解
  • LeetCode 19: 删除链表的倒数第 N 个结点
  • 捡捡java——4、日志
  • 数据结构:单链表的应用(力扣算法题)第二章