【C++】2.9异常处理

目录

[1. 抛出异常](#1. 抛出异常)

[2. 栈展开](#2. 栈展开)

[3. 查找匹配代码](#3. 查找匹配代码)

应用示例

模拟发送函数

[4. 安全问题](#4. 安全问题)

[5. 异常规范](#5. 异常规范)


1. 抛出异常

  • 异常抛出后,沿着调用链,在里层或外层都能被处理。

  • 捕获 (catch) 规则:由作用链上类型匹配最近的捕获。

cpp 复制代码
int func() {
    int a; int b;
    cin >> a >> b;
    try {
        if (b == 0) {
            string s("divide by 0");
            throw s;
        } else {
            return a / b;
        }
    }
    catch (int s) {
        cout << s << endl;
    }
}

int main() {
    try {
        cout << func();
    }
    catch (const string& s) {
        cout << s << endl;
    }
    catch (...) {
        cout << "unknown" << endl;
    }
    return 0;
}
  • 在函数中,若输入 1, 0,由于函数内最近的 catch 要求 int 类型,不匹配,因此继续向下抛出,到 main 函数中匹配 string 类型的捕获。

  • catch (...) 可以捕获任何类型的异常,作为程序的兜底,防止因为异常找不到匹配的 catch 而崩溃。

2. 栈展开

  • 抛出异常后,由于后面的程序不执行,函数栈帧会不断销毁,直到找到最近的可接受该异常的处理代码。

  • 若直到 main 函数都没找到可以 catch 的,程序就会直接报错。

3. 查找匹配代码

  • 规则 :多个 catch 匹配就选更近的;允许常量转为非常量;允许数组转为指针;允许子类转为父类。

  • 在大型项目中,一般会选用子类转为父类的规则。

应用示例

cpp 复制代码
class basemod {
public:
    basemod(const string& errmsg, int id)
        :_errmsg(errmsg)
        , _id(id) {
    }
    virtual string what() const {
        return _errmsg;
    }
    int getid() const {
        return _id;
    }
protected:
    string _errmsg;
    int _id;
};

class Amod :public basemod {
public:
    Amod(const string& errmsg, int id, const string& data)
        :basemod(errmsg, id)
        , _data(data) { }
    virtual string what() const {
        string str = "Amod";
        str += _errmsg;
        str += "->";
        str += _data;
        return str;
    }
protected:
    string _data;
};
  • 假设在这个项目中有 A 模块(发送模块)继承了基类。what 函数用于生成报错信息,id 用于存储报错值。
cpp 复制代码
catch (const basemod& b) {
    cout << b.what() << endl;
}
  • 由于 what 是虚函数,因此不同的子类在通过基类引用捕获时,可以调用子类重写的 what 函数,生成不同的报错信息。

模拟发送函数

cpp 复制代码
void send() {
    string mes;
    cin >> mes;
    for (int i = 0; i < 2; i++) {
        try {
            if (rand() % 3 == 0) {
                throw Amod("网络不稳定", 102, mes);
            }
            if (rand() % 3 == 1) {
                throw Amod("不是对方好友", 103, mes);
            }
            cout << "成功" << endl;
            break;
        }
        catch (const basemod& b) {
            if (b.getid() == 102) {
                if (i == 1) throw;
                cout << "第" << i + 1 << "次尝试" << endl;
            } else {
                throw;
            }
        }
    }
}
  • 假如我们发送可能遇到两种报错:网络不好以及不是对方好友。

  • 网络不好就尝试重新发送,再不行才抛出;不是对方好友则直接抛异常。

  • 因此执行循环,当没有报错直接 break,有报错则进入 catch (const basemod& b) 处理,并根据情况继续抛出。

  • 同时,其它模块也可以继承基类,抛出对应的异常。

4. 安全问题

  • 在抛出异常后,后面的代码不再执行,可能导致内存释放不会进行。

  • 解决方式:使用智能指针。

5. 异常规范

  • C++98:如果一个函数不会抛出异常,就在声明后加 throw()(跟空括号)。

  • C++11:加 noexcept

  • 但由于 noexcept 可能与实际异常捕获冲突,且编译器不会严格检查,因此要避免写这种有冲突的代码。

  • 同样,noexcept 可以检查这个函数是否会抛异常。

cpp 复制代码
cout << noexcept(func()) << endl;
int t = 0;
cout << noexcept(t++) << endl;
  • 可能会抛异常的函数返回 0,否则返回 1。
相关推荐
CoovallyAIHub1 小时前
如何用10%的标注数据,达到可媲美全监督模型的性能?AAAI 2026论文揭秘BCSI三大创新设计
深度学习·算法·计算机视觉
古城小栈1 小时前
Rust unsafe 一文全功能解析
开发语言·后端·rust
没有bug.的程序员1 小时前
Java IO 与 NIO:从 BIO 阻塞陷阱到 NIO 万级并发
java·开发语言·nio·并发编程·io流·bio
无情的8861 小时前
S11参数与反射系数的关系
开发语言·php·硬件工程
AIFQuant1 小时前
2026 澳大利亚证券交易所(ASX)API 接入与 Python 量化策略
开发语言·python·websocket·金融·restful
玖釉-1 小时前
[Vulkan 学习之路] 19 - 顶点缓冲区:顶点输入描述 (Vertex Input Description)
c++·windows·图形渲染
肆悟先生2 小时前
3.18 constexpr函数
开发语言·c++·算法
别在内卷了2 小时前
三步搞定:双指针归并法求两个有序数组的中位数(Java 实现)
java·开发语言·学习·算法
txinyu的博客2 小时前
select/poll/epoll
linux·c++