C++异常处理

异常处理

C++提供异常处理,可以使得问题的检测与问题的解决分离,这样在开发时,只需关注如何将问题抛出来,不用考虑针对所抛出的问题需要采取怎样的解决办法。总结而言,C++的异常处理思想是一个工程类思想,是希望开发者关注于程序的主体功能实现。

Try块

C++异常处理包括:

  1. throw表达式,错误检测部分使用这种表达式来说明遇到了不可处理的错误。可以认为,throw的行为相当于引发了一个异常条件,是异常的发生原点。
  2. try块,错误处理部分使用它来处理异常。由一个try{}表达式和若干个catch{}子句构成。在try块中执行的代码所抛出的异常,通常会被其中的一个catch子句处理,因此catch子句通常也被称为异常处理代码。
  3. 由标准库定义的一组异常类,用来在throw和相应的catch之间传递有关的错误信息。标准库异常类定义在四个头文件中:
  • excepttion头文件定义了最常见的异常类,这个类只通知异常的产生,但不会提供更多的信息。换句话说,exception类仅是表明其为一个异常类型,具体的信息需要由调用者具体来指明,exception异常类本身不具有特别明确的详细的指向性。
  • stdexcept头文件定义了几种常见的异常类。
    在这里插入图片描述
  • new头文件定义的bad_alloc
  • type_info头文件定义的bad_cast

标准库异常类只提供很少操作,包括创建、复制异常类型对象以及异常类型对象的赋值。exception、bad_alloc以及bad_cast类型只提供默认的构造函数,无法创建带参数的对象。其他类型的异常只定义了一个带有string初始化式的构造函数,用于为所发生的错误提供解释信息。异常类型只提供了一个what操作。

抛出类类型的异常

  • 异常是通过抛出对象而引发的,该对象的类型决定应该激活哪一个处理代码。被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那个。
  • 异常以类似于将实参传递给函数的方式抛出和捕获,因此,catch子句捕获到的异常是throw抛出的类对象的一个副本。
  • 而异常实例交给catch子句时,也有多种形式,一种是catch(cls),一种是catch(cls &),即引用与否的差别。如果是引用的形式,那么catch子句中所使用的异常对象是引用的throw对象的副本;如果是非引用的,则catch子句中使用的对象是throw对象的副本的副本。
  • 通常如果catch子句处理因继承而相关的类型的异常,他应该使用引用的形式。引用类型是直接访问异常对象本身的。如果异常说明符不是引用,则catch对象是异常对象的副本,如果catch对象是基类而异常对象是派生类,就将会分割。
  • throw执行了,相当于将函数的控制权由throw的现场交给了处理代码,此时需要注意两点:(1)throw之后的代码都不会再被执行,相当于提前退出;(2)在处理异常的时候,抛出异常的块中的局部变量可以被回收,这是由于控制权的转移,局部变量弹栈所触发的,但是堆上的变量则不能够被回收,需要单独特殊处理。
  • 对于throw块中堆上的变量不能够很好的处理,推荐使用智能指针来解决。

异常与指针

  • 在抛出中对指针解引用比较麻烦。因为此过程涉及到多态,指针解引用的结果是一个对象,其类型与指针的类型相匹配。如果指针指向了一个继承关系中,指针所指向的对象类型就有可能和指针的类型不相同。
  • 无论对象的实际类型是什么,异常对象的类型都是与指针的静态类型相匹配的,如果该指针指向了一个派生类对象的基类指针,那么就会指向一个基类对象,因此会产生分割,造成泄漏或者是指向错误。
  • 如果抛出的是指针,则会因为指针所指向的内存的性质而产生不同的结果,如果指针指向的是一个局部内存,那么就会引发不可想象的后果。
  • 抛出指针通常是个坏主意,抛出指针要求在对应处理代码存在的任意地方存在指针所指向的内存对象。

栈展开

  • 抛出异常的时候,将暂停当前函数的执行,开始查找匹配的catch子句。首先检查throw本身是否在try块内部,如果是,检查与该try相关的catch子句,查看是否有与之匹配的,有就处理,没有就退出当前函数并释放当前函数的局部资源,并继续在调用函数中查找。
  • 当catch结束的时候,控制权需要再次转换,需要恢复到紧接在该try块相关的最后一个catch子句之后的点继续执行,即回到了原throw处的之后的最近catch处理模块之后。
  • 如果所有的catch都没有匹配上,则会调用标准库的terminate函数,一般而言,terminate函数会调用abort函数强制退出程序。

构造与析构

  • 析构函数应该从不抛出异常。因为异常处理通常是要调用析构函数的,如果被调用的析构函数又抛出异常,那么如果析构函数所抛出的异常是一个未经处理的,就会调用terminate,这样的话,就会导致很多资源的析构函数没有被执行,会导致泄漏。
  • 标准库中的代码都是想办法避免析构处有异常发生。
  • 构造函数是需要抛出异常的。

重新抛出

  • 在catch子句或者catch子句内的函数中,throw;不带任何异常类型
  • 仍然将一个异常对象延链向上传递,被抛出的异常是原来的异常对象,而不是catch子句的形参。
  • 一般而言,catch可以改变他的形参。在改变他的形参之后,如果catch重新抛出异常,那么,只有当异常说明符是引用的时候,才会传播这些改变。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值