✅ 一、异常处理基本语法(try-catch)

try {
    // 可能抛出异常的代码
} catch (const std::exception& e) {
    // 捕获异常
} catch (...) {
    // 捕获所有类型的异常
}

✅ 二、throw 的用法

抛出异常对象,一般是值(可为内置类型、类对象)--->也就是后面跟着类、对象、值都可以

throw std::runtime_error("error");
throw 1;         // 抛出 int 类型异常
throw MyError(); // 抛出用户自定义类

可重新抛出当前异常:

catch (...) {
    // 做点处理
    throw; // 重新抛出
}

✅ 三、异常匹配规则(按顺序匹配)

会按 catch **顺序查找**最先匹配类型 catch by value 会导致 slicing(对象切片),推荐使用 catch by reference


面试常问点总结(口诀式记忆)“三块一安全”

✔ 三块结构(try-catch-throw)

  1. try 块
    用于包裹可能抛出异常的代码段。必须紧跟至少一个 catch,否则编译错误

  2. throw 语句
    在某些条件下显式抛出异常。可以抛出任何类型的对象(内置类型、自定义类等)。

  3. catch 块
    捕获异常对象,进行处理,防止程序崩溃。按类型顺序匹配,第一个匹配的执行。
    (🚨 注意:必须捕获引用类型,防止切片)

✔ 一机制(栈展开 + 析构)

当异常抛出但未被当前函数捕获时,会发生栈展开

  1. 栈展开(Stack Unwinding)
    异常沿着调用链回溯查找匹配的 catch。每经过一层函数返回,当前作用域的局部对象都会被自动销毁(即调用其析构函数)。

  2. 自动调用析构函数(即 RAII 的基础)
    栈展开过程中,所有已构造完成的局部对象都会被正确析构。保证不会发生资源泄漏(前提是资源绑定在对象上)。

✔ 安全原则(RAII + 不在析构抛异常)

异常机制要想安全可靠,必须遵循以下两大原则:

  1. RAII(Resource Acquisition Is Initialization)
    用对象的生命周期管理资源,构造函数获取资源、析构函数释放资源。
    避免手动 new/delete、malloc/free。
    遇到异常时,不依赖显式清理逻辑,栈展开自动处理。

  2. 析构函数中不能抛异常
    若栈展开期间析构函数再抛出异常,会导致程序调用 std::terminate() 强制终止。
    所以 C++11 开始,析构函数必须被声明为 noexcept。

编译时错误

🔚 总结:C++ 异常相关编译时错误常见来源

类型 示例 会报错?
语法错误 缺失 catch、括号错误
抛出不完整类型 throw Incomplete();
函数异常规范冲突 noexcept 函数抛异常 ✅(逻辑或运行时)
对象构造失败 异常对象构造器再抛异常 ✅(运行时崩溃)
访问权限错误 捕获后访问私有成员
类型不匹配 catch 捕获错误类型 ✅/❌(未匹配到会 terminate)

口诀:抛拷问权限,捕错需括号;析构慎 throw,异常需头包;noexcept守信,匹配要周到。