C++ 异常处理


一、概述

1.1 程序中的两类错误

错误类型 说明 捕获时机
语法错误 代码不符合语言规范 编译时(编译系统发现)
运行错误 程序逻辑引发的意外(如下标越界、内存分配失败) 运行时

1.2 为什么需要异常处理

在设计程序时,应当事先分析程序运行时可能出现的各种意外情况,制订出相应的处理措施,这就是程序的异常处理任务。

  • 没有异常处理:运行出现异常时,程序只能终止运行
  • 有异常处理:程序按照预设的处理流程,转到异常处理代码段执行,而不会崩溃

1.3 C 语言 vs C++ 的异常处理方式

语言 处理方式
C 通常由被调用函数返回一个数值作为错误标记
C++ 函数可识别异常条件,通过 throw 抛出异常,由 catch 捕获处理

二、三个关键字:try / catch / throw

异常提供了一种转移程序控制权的方式。C++ 异常处理涉及三个关键字:

throw  →  抛出异常(当问题出现时)
  ↓
try    →  保护代码块(放置可能抛出异常的代码)
  ↓
catch  →  捕获并处理异常
关键字 作用
throw 当问题出现时,程序抛出一个异常
catch 在想要处理问题的地方,通过异常处理程序捕获异常
try 标识将被激活的特定异常的代码块,后面通常跟着一个或多个 catch

try 块中放置可能抛出异常的代码,称为保护代码


三、示例代码

从键盘输入两个数,实现相除:

#include <iostream>
using namespace std;

/*
* 从键盘输入两个数,实现相除
*/

int main()
{
    double x, y;
    cout << "请输入xy的值";
    cin >> x >> y;

    try {
        if (y == 0)
            throw -1;        // 抛出 int 类型异常
        else if (x == 0)
            throw -1.0;      // 抛出 double 类型异常
        else
            cout << "x/y=" << x / y << endl << endl;
    }
    catch (int e) {
        cout << "catch(int):" << e << endl;
    }
    catch (double d) {
        cout << "catch(double):" << d << endl;
    }

    return 0;
}

运行结果

代码解析

try {
    if (y == 0)
        throw -1;       // ① 除数为 0 → 抛出 int 异常 → 跳到 catch(int)
    else if (x == 0)
        throw -1.0;     // ② 被除数为 0 → 抛出 double 异常 → 跳到 catch(double)
    else
        cout << "x/y=" << x / y << endl;  // ③ 正常执行
}
catch (int e)    { /* 捕获 int 异常 */ }
catch (double d) { /* 捕获 double 异常 */ }
输入 抛出的异常类型 匹配的 catch
y = 0 throw -1(int) catch(int e)
x = 0 throw -1.0(double) catch(double d)
x, y 均非 0 不抛出异常,正常计算 不走 catch

四、执行流程

程序正常执行
    ↓
进入 try 块
    ↓
  是否抛出异常?
   /        \
 否          是
  ↓           ↓
正常执行   匹配 catch 块
  ↓           ↓
跳过 catch  执行异常处理代码
              ↓
           程序继续

一旦抛出异常,try 块中抛出点之后的代码不会被执行,控制权立即转移到匹配的 catch 块。


五、补充知识点

5.1 标准异常类

C++ 标准库提供了异常类层次结构,位于 <stdexcept> 头文件中:

#include <stdexcept>

try {
    throw std::runtime_error("运行时出错");
}
catch (const std::exception& e) {
    cout << e.what() << endl;  // 输出异常信息
}

5.2 catch 的匹配规则

  • catch 按照书写顺序从上到下匹配
  • 派生类异常应写在基类异常前面(否则会被基类 catch 截获)
  • catch(...) 可以捕获所有类型的异常(通常放在最后)
try {
    // ...
}
catch (const std::runtime_error& e) { /* 先匹配派生类 */ }
catch (const std::exception& e)     { /* 再匹配基类   */ }
catch (...)                         { /* 兜底捕获所有 */ }

5.3 异常重新抛出

在 catch 块中可以用 throw;(不加参数)将异常继续向外抛出:

catch (int e) {
    cout << "记录日志:" << e << endl;
    throw;  // 重新抛出,交给上层处理
}

更多推荐