C++ 异常 完整讲解

C++ 异常 完整讲解

C++ 异常是程序运行时错误处理机制 ,用于分离错误抛出错误处理代码,替代传统返回值判错,让代码结构更清晰、容错性更强。

一、基本语法

核心三关键字:

  • throw:主动抛出异常
  • try:包裹可能出错的代码块
  • catch捕获并处理对应类型的异常

基础格式

cpp 复制代码
try {
    // 正常业务代码,可能抛出异常
    throw 异常数据; 
}
catch(异常类型 变量) {
    // 捕获并处理异常
}

二、简单示例

1. 抛出、捕获基本类型异常

cpp 复制代码
#include <iostream>
using namespace std;

void divide(int a, int b) {
    if (b == 0) {
        // 抛出 int 类型异常
        throw 100; 
    }
    cout << a / b << endl;
}

int main() {
    try {
        divide(10, 0);
    }
    // 捕获 int 类型异常
    catch (int err) {
        cout << "捕获异常,错误码:" << err << ",除数不能为0" << endl;
    }
    return 0;
}

2. 抛出字符串异常

cpp 复制代码
void test() {
    throw "参数非法";
}

int main() {
    try {
        test();
    }
    catch (const char* msg) {
        cout << "异常信息:" << msg << endl;
    }
    return 0;
}

三、多异常捕获(多个 catch)

一个 try 可搭配多个 catch ,按从上到下顺序匹配,匹配到即执行,不再向后匹配。

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

void func(int num) {
    if (num == 0)
        throw 0;
    else if (num == 1)
        throw "数值为1";
    else
        throw string("未知错误");
}

int main() {
    try {
        func(1);
    }
    catch (int e) {
        cout << "捕获int异常:" << e << endl;
    }
    catch (const char* e) {
        cout << "捕获字符串异常:" << e << endl;
    }
    catch (string e) {
        cout << "捕获string异常:" << e << endl;
    }
    return 0;
}

万能捕获 catch(...)

... 可以捕获所有类型异常 ,必须放在所有 catch最后面

cpp 复制代码
try {
    // 任意出错代码
}
catch (int) { }
catch (const char*) { }
catch (...) {
    cout << "捕获未知类型异常" << endl;
}

四、异常的传递(跨函数传播)

异常不会被函数直接消化

  1. 当前函数没有 try-catch,异常会向上层调用函数传递
  2. 逐层向上,直到被 catch 捕获
  3. 最终没人捕获,程序直接终止
cpp 复制代码
void A() { throw "A函数出错"; }
void B() { A(); }
void C() { B(); }

int main() {
    try {
        C();
    }
    catch (const char* e) {
        cout << e << endl;
    }
    return 0;
}

五、C++ 标准异常类(重点)

C++ 内置一套标准异常体系 ,定义在头文件 <stdexcept>,所有标准异常都继承自 std::exception

1. 常用标准异常

异常类 说明
exception 所有标准异常基类
runtime_error 运行时异常(运行阶段出错)
logic_error 逻辑错误(代码设计问题)
out_of_range 下标越界
invalid_argument 无效参数
overflow_error 数值溢出

2. 基类核心方法

what():返回异常描述字符串,虚函数,子类可重写。

3. 使用标准异常示例

cpp 复制代码
#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
    try {
        throw out_of_range("数组下标越界啦");
    }
    // 捕获基类 exception,可接收所有子类异常
    catch (const exception& e) {
        cout << "异常信息:" << e.what() << endl;
    }
    return 0;
}

建议:捕获时使用 引用 const exception&,避免拷贝、防止切片。


六、自定义异常类(工程常用)

继承 std::exception,重写 what() 实现自定义异常。

cpp 复制代码
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

// 自定义异常类
class MyException : public exception {
private:
    string msg;
public:
    MyException(const string& s) : msg(s) {}
    // 重写 what 方法
    const char* what() const noexcept override {
        return msg.c_str();
    }
};

void test() {
    throw MyException("自定义业务异常:权限不足");
}

int main() {
    try {
        test();
    }
    catch (const MyException& e) {
        cout << e.what() << endl;
    }
    return 0;
}
  • noexcept:C++11 及以上,表示该函数不会抛出异常

七、异常接口声明(了解)

C++ 允许声明函数允许抛出哪些异常,现代 C++ 已逐渐弱化:

  1. void f() throw(); // C++03:不抛出任何异常
  2. void f() throw(int); // 只允许抛出 int 异常

C++11 推荐使用 noexcept 关键字:

cpp 复制代码
void func() noexcept; // 保证函数不抛异常

八、异常与析构函数

析构函数绝对不要抛出异常

原因:

  1. 异常触发时栈展开会自动调用析构函数
  2. 若析构函数再抛异常,程序会直接崩溃
    规范:析构函数必须加 noexcept

九、栈展开(Stack Unwinding)

抛出异常后,程序会从抛出点向上回溯

  1. 依次销毁当前作用域内的局部对象(调用析构)
  2. 直到找到匹配的 catch
  3. 这个过程称为栈展开

十、noexcept 关键字(C++11 核心)

  1. 修饰函数:表明函数不会抛出异常
  2. 编译器会做检查,违反则终止程序
  3. 作用:提升性能、保证容器/移动语义安全
cpp 复制代码
void safeFunc() noexcept {
    // 这里不能 throw
}

十一、异常优缺点总结

优点

  1. 错误处理与业务代码分离,可读性强
  2. 异常可跨多层函数传递,不用每层判断返回值
  3. 支持标准异常、自定义异常,分类清晰

缺点

  1. 有一定性能开销(栈展开、对象构造析构)
  2. 流程跳转复杂,调试难度略高
  3. 不能用于嵌入式、高实时性等对性能极致要求的场景

十二、使用规范(面试/工程建议)

  1. 优先使用 C++ 标准异常,业务场景再自定义异常
  2. 捕获使用引用 const 异常类&,避免拷贝和切片
  3. catch(...) 只做兜底,尽量精确捕获异常类型
  4. 析构函数、移动构造/移动赋值必须 noexcept,禁止抛异常
  5. 不要在循环、高频调用场景滥用异常(性能差)
  6. 异常只处理运行时错误,正常逻辑不要用异常跳转
相关推荐
AI科技星2 小时前
数术工坊 · 第四卷 橡皮泥江湖(拓扑学)【完整定稿】
c语言·开发语言·汇编·electron·概率论·拓扑学
张忠琳3 小时前
【Go 1.26.4】Golang Select 深度解析
开发语言·后端·golang
AC赳赳老秦4 小时前
OpenClaw+Power Apps 实战:自动生成 Power Apps 应用、连接 Excel 数据源
大数据·开发语言·python·serverless·excel·deepseek·openclaw
提笔了无痕4 小时前
如何用Go实现整套RAG流程
开发语言·后端·golang
(Charon)4 小时前
【C++ 面试高频基础:指针、引用、const、static、new/delete 总结】
java·开发语言
2601_961875245 小时前
法考考试时间安排及科目|时间表|资料已整理
开发语言·c#·inverted-index·suffix-tree·sstable·r-tree·lsm-tree
AI科技星5 小时前
数术工坊第八卷:算力革命
c语言·开发语言·网络·量子计算·agi
Frank学习路上5 小时前
【C++】面试:关键字与语法特性
c++·面试
geovindu5 小时前
go: Generators Pattern
开发语言·后端·设计模式·golang·生成器模式