欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > 【C++】异常

【C++】异常

2025/5/3 19:45:21 来源:https://blog.csdn.net/tan_run/article/details/147602955  浏览:    关键词:【C++】异常

📝前言:
这篇文章我们来讲讲C++异常

🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏,Linux


文章目录

  • 一,异常处理
    • 1.1 基本语法
      • 1.1 抛出异常
      • 1.2 捕获异常
  • 二,具体的找catch的栈展开过程
  • 三,catch 的类型匹配
  • 四, 异常的重新抛出
  • 五,异常安全问题
  • 六,异常的规范
  • 七,标准异常

一,异常处理

异常处理是C++中处理运行时错误的重要机制,它允许程序在遇到错误时能够优雅地恢复或终止,而不是直接崩溃。

1.1 基本语法

C++异常处理使用三个关键字:try, catch, 和 throw

try {// 可能抛出异常的代码if (error_occurred) {throw exception_object;}
} catch (exception_type1& e) {// 处理exception_type1类型的异常
} catch (exception_type2& e) {// 处理exception_type2类型的异常
} catch (...) {// 处理所有其他类型的异常
}

1.1 抛出异常

使用throw关键字抛出异常:

double divide(double a, double b) {if (b == 0.0) {throw "Division by zero!";  // 抛出字符串字面量}return a / b;
}

1.2 捕获异常

使用catch块捕获并处理异常:

try {double result = divide(10.0, 0.0);
} catch (const char* msg) {cerr << "Error: " << msg << endl;
}
  • 程序先执行try里面的语句,如果try里面throw了异常,throw后⾯的语句将不再被执行。程序的执行从throw位置跳到与之匹配的catch模块
  • catch的规则:找调用链中与该对象类型匹配离抛出异常位置最近的那⼀个
  • throwcatch:1,沿着调用链的函数可能提早退出。2、沿着调用链创建的对象都将销毁
  • 抛出异常对象后,会⽣成⼀个异常对象的拷⻉,因为抛出的异常对象可能是⼀个局部对象,所以会⽣成⼀个拷⻉对象,这个拷⻉的对象会在catch⼦句后销毁。(这⾥的处理类似于函数的传值返
    回)

二,具体的找catch的栈展开过程

  • 如果throwtry里面,则找对应的catch
  • 如果没找到 / 类型不匹配,则提前退出当前函数,到外层函数里面(函数调用链)找
  • 如果找到main函数都没找到,则程序会调⽤标准库的 terminate 函数终止程序【main函数兜底】
  • 如果这个过程中有一步找到了,则跳去执行catch,执行完以后,不会回到原来的位置,而是继续执行catch后面的语句,相当于throw改变了执行流程

在这里插入图片描述

三,catch 的类型匹配

除了严格的完全对应的类型匹配,以下几种也可以匹配:

  • 允许从⾮常量向常量的类型转换,也就是权限缩⼩
  • 允许数组转换成指向数组元素类型的指针,函数被转换成指向函数的指针
  • 允许从派⽣类向基类类型的转换(实际中继承体系基本都是⽤这个⽅式设计)
  • catch(...)...表示匹配任意类型(但是不知道异常错误是什么),main函数的兜底就是这个

四, 异常的重新抛出

在catch块中可以重新抛出当前异常:

try {// ...
} catch (const std::exception& e) {// 部分处理throw;  // 重新抛出相同的异常
}
  • 重新抛出的是原始异常对象(不是拷贝),并且保留原始的异常类型和内容。
  • 不会被同一个catch块再次捕获,而是会跳出当前try-catch块,继续向外层传播,由外层的try-catch块捕获。

五,异常安全问题

  • 异常抛出后,后⾯的代码就不再执行,如果前面申请了资源,但是释放代码在catch后面,而catch中又重新抛出了异常,就会没有释放,引发资源泄漏,产⽣安全性的问题。解决方法:1,捕获异常以后,要先释放资源,再重新抛出;2,用智能指针的RAII方式解决这种问题是更好的
  • 其次,析构函数中,如果抛出异常也要谨慎处理,比如析构函数要释放10个资源,释放到第5个时抛
    出异常,则也需要捕获处理,否则后⾯的5个资源就没释放,也资源泄漏了。

示例(解决方法1):

void process() {int* resource = new int(100);  // 申请资源(假设是动态内存)try {some_operation_that_may_throw();  // 可能抛出异常} catch (...) {delete resource;  // 手动释放资源throw;           // 重新抛出异常}delete resource;  // 正常情况下的释放
}

但是这种解决方法不好,后面用智能指针 +RAII更好!

六,异常的规范

  • 对于用户和编译器而言,预先知道某个函数是否会抛出异常有助于简化调用函数的代码
  • noexcept关键字:函数参数列表后⾯加noexcept表示不会抛出异常,啥都不加表示可能会抛出异常
  • 编译器并不会在编译时检查noexcept修饰的函数。即使里面还是有throw
  • 但是,如果⼀个声明了noexcept的函数抛出了异常(里面有throw,并且运行时抛出了),程序会调用 terminate 终止程序
  • noexcept还可以作为⼀个运算符去检测⼀个表达式是否会抛出异常,如果保证不会抛出异常就返回true,可能会就返回false

示例(有noexcept但是还是会throw):

double Divide(int a, int b) noexcept
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
int main()
{try{int len, time;cin >> len >> time;cout << Divide(len, time) << endl;}catch (const char* errmsg){cout << errmsg << endl;}catch (...){cout << "Unkown Exception" << endl;}int i = 0;cout << noexcept(Divide(1, 2)) << endl;cout << noexcept(Divide(1, 0)) << endl;cout << noexcept(++i) << endl;return 0;
}

编译时通过,运行时,输入:1 0,直接干没了
在这里插入图片描述
不带noexcept

double Divide(int a, int b)

输入2 2 运行结果:
在这里插入图片描述

七,标准异常

官方文档:https://legacy.cplusplus.com/reference/exception/exception/

  • C++标准库也定义了⼀套自己的⼀套异常继承体系库,基类是exception,所以我们⽇常写程序,需要在主函数捕获exception即可,要获取异常信息,调⽤what函数,what是⼀个虚函数,派生类可以重写。
  • 标准异常头文件;<stdexcept>

在这里插入图片描述
标准库常见异常类:

异常类用途
std::logic_error程序逻辑错误(如无效参数)
std::runtime_error运行时错误(如文件不存在)
std::bad_alloc内存分配失败(new 抛出)
std::out_of_range越界访问(如 vector::at)

示例:

int main() {try {// 尝试执行的代码块std::vector<int> v(1000000000); // 可能抛出 std::bad_allocthrow std::runtime_error("自定义运行时错误"); // 构造并抛出标准异常类:runtime_error异常,并且用"自定义运行时错误"构造它,把异常信息设置成这句话。后续.what()的时候,就可以获取异常信息}catch (const std::exception& e) {// 异常处理块std::cerr << "捕获异常: " << e.what() << std::endl;}return 0;
}

执行流程

  1. try 块中的代码执行:

    • 首先尝试创建一个超大vector v(1000000000)

      • 如果系统内存不足,会抛出 std::bad_alloc 异常

      • 如果创建成功,继续执行下一行

    • 然后执行 throw std::runtime_error("自定义运行时错误")

      • 这行代码会主动抛出一个运行时错误异常
  2. catch 块捕获异常:

    • 无论抛出的是 std::bad_alloc 还是 std::runtime_error

    • 因为它们都继承自 std::exception,所以都会被这个catch块捕获

    • 捕获后执行异常处理代码,打印错误信息

运行示例(内存足够时):
在这里插入图片描述
vector的大小再加大(内存不足时):
在这里插入图片描述


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词