当前位置: 首页 > news >正文

异常处理 || 抛出 || 捕获 || noexcept || 异常类

处理大型程序的3大特性:

  1. 异常处理:协同处理错误
  2. 命名空间:协同开发
  3. 多重继承:复杂建模

异常处理

  1. 异常处理机制:允许在运行时对出现的问题相应处理,它将检测和处理分离开,无需知道处理的具体细节
  2. throw表达式,抛出 || 引发 异常 :创建异常对象
  3. try块{ throw表达式}:接受抛出异常的部分
  4. 异常对象传递给异常声明 参数,并匹配成功后,执行这个catch块
  5. catch子句块(异常声明 形参列表){ 函数体 }:捕获异常:捕获try中的throw表达式,函数体:处理异常
  6. catch一个或多个……

抛出异常

  1. throw表达式:必须拥有完全类型,如果是类类型,必须含有public的折构和拷贝构造或移动构造(这样才能被销毁和初始化异常对象)
#include <iostream>
#include <stdexcept>void func3() {std::cout << "Entering func3\n";throw std::runtime_error("Error in func3"); // 抛出异常std::cout << "This line in func3 will not be executed\n";//不会被执行
}void func2() {std::cout << "Entering func2\n";func3(); // 调用 func3std::cout << "This line in func2 will not be executed\n";//不会被执行
}void func1() {std::cout << "Entering func1\n";func2(); // 调用 func2std::cout << "This line in func1 will not be executed\n";//不会被执行
}int main() {try {func1(); // 调用 func1} catch (const std::exception& e) {std::cout << "Caught exception: " << e.what() << std::endl;}return 0;
}//输出
Entering func1
Entering func2
Entering func3
Caught exception: Error in func3
  1. 调用链函数可能会提早结束,因为throw后的语句块不会执行
  2. 栈展开过程中,一层一层往上返回,在每一层函数返回时,所有局部对象的析构函数都会被调用,从而销毁这些对象。

栈展开

  1. 栈展开:在跳转到 catch 块之前,会沿着嵌套函数的调用链不断查找,逐层退出当前函数的调用栈,直到找到一个合适的catch块来处理该异常
  2. 调用链:在整个嵌套过程中形成的调用链),并创建一系列的对象,比如从main开始调用func1,func1又调用func2
  3. 一下是具体过程:
  4. 当throw位于try语句块内,并当执行throw引发异常后,暂停当前函数的执行(类似于return),跟在throw后面的代码将不会被执行,
  5. 而是会寻找匹配与try关联的catch模块,如果找到了第一个匹配到的catch,就执行这个catch块,当执行完毕,找到try关联的最后一个catch的语句块结束,从这里开始执行
  6. 如果这一步没有找到,并且try嵌套在其他try语句块内,从外层try继续寻找
  7. 如果还是没有,退出当前函数,在调用函数的外层函数继续寻找
  8. 如果最终没有找到匹配,调用标准库函数terminate终止程序,因为异常通常被认为妨碍程序执行的事件
  9. //
  10. 自动销毁:
  11. 在栈展开过程中,会逐层执行代码块,其中创建的局部对象:局部内置类型,局部类对象(折构函数),都会被正确销毁,并且有可能发生在构造函数中,数组,容器的初始化中,初始化的这部分元素,也都会被正确销毁
  12. //
  13. 资源释放管理:
  14. 在栈展开过程中,
  15. 对于普通函数来讲,如果函数体内抛出异常,函数中throw之后的语句不会执行,很有可能释放资源的部分未被执行
  16. 如果用类管理资源,即使发生了异常,折构函数总是会被执行,这样就可以保证资源都被正确释放
  17. 但要注意,折构函数不要抛出自己不能处理了的异常,因为一旦自身没能捕获异常,那么程序将终止

捕获异常

  1. 异常对象:
  2. 使用throw表达式对异常对象拷贝初始化,当异常处理完毕,将被销毁
  3. 表达式的静态类型,决定异常对象的类型
  4. //
  5. catch参数:必须拥有完全类型,仅可以为左值引用
  6. 异常对象会初始化异常声明的参数
  7. 异常声明的参数与引用:
  8. 非引用类型会发生拷贝,且在继承关系中,用派生类初始化基类类型,则派生类中特有的数据和行为将会丢失。
  9. 而如果为引用类型,则不会被拷贝,也不会丢失。
  10. //
  11. 匹配规则:
  12. 从异常对象转为catch参数,支持的有限的转换,否则必须精准匹配:
    1. 非常量转为常量
    2. 派生类转为基类
    3. 数组或函数转为指向数组或函数的指针
  13. //
  14. 重新抛出异常
  15. throw后面不包含任何表达式,且位于catch块内,
  16. 表明结束当前的try块,沿着调用链上一层(外层),继续处理异常,比较像break
  17. //
  18. 捕获所有异常
  19. catch( ...),通常和throw重新抛出异常,以及多个子catch块一起使用,

noexcept

  1. 指特定函数不会发生异常
  2. ……续

异常类层次

  1. exception头文件定义了通用异常类,是所有异常类的基类,仅报告异常发生,不提供其他行为,
  2. std::bad_alloc表示内存分配失败,std::bad_cast表示动态类型转换失败,std::bad_typeid表示对空指针类型的 typeid 操作
  3. stdexcept定义常用的异常类:std::runtime_errorstd::logic_error
  4. std::runtime_error:

    • 表示在运行时检测到的问题。
    • 常见派生类:
      • std::overflow_error: 表示算术运算的溢出。
      • std::underflow_error: 表示算术运算的下溢。
      • std::range_error: 表示范围错误。
      • std::logic_error: 表示逻辑错误。
      • std::domain_error: 表示数学函数的域错误。
      • std::invalid_argument: 表示无效的函数参数。
  5. std::logic_error:

    • 表示程序逻辑错误,通常在编译时就可以检测到。
    • 常见派生类:
      • std::invalid_argument: 表示传递给函数的无效参数。
      • std::domain_error: 表示参数超出允许的数学函数域。
      • std::length_error: 表示尝试创建过长的对象。
      • std::out_of_range: 表示使用超出范围的值。
  6. 我们可以自定义异常类,继承字标准异常类,并重写 what() 函数以提供特定的异常信息。
  7. ……续

http://www.mrgr.cn/news/9428.html

相关文章:

  • C语言第17篇
  • Android中实现WebView的秒开场景及方案
  • OSPF与BGP防环机制总结
  • flutter开发小技巧
  • ffplay源码分析(二)结构体VideoState
  • 电脑U口管理软件分享|U口管理软件哪个好?
  • 交叉编译Qt5.12.8附带编译opengl
  • 编程思想:编程范式:面向对象
  • 递归搜索与回溯专题篇一
  • 目标检测多模态大模型实践:貌似是全网唯一Shikra的部署和测试教程,内含各种踩坑以及demo代码
  • 幂等方案分析
  • chrome扩展程序本地打包
  • 流体中的流线【StreamLines】的实现
  • mysql数据库----简单认识库的操作
  • 绝了!在vscode中体验《黑神话:悟空》的视觉冲击
  • 【Tools】 Git 的基本概念和使用方式
  • 下载了pytorch 为什么导包是 torch
  • Transformer模型:Position Embedding实现
  • 如何在 macOS 上升级 Ruby 版本
  • rust web 使用 POSTGRESQL