关于C++的“抛出异常”机制:

关键点:
(1)throw是将抛出的表达式的值拷贝到“异常对象”中,catch则是根据异常对象进行参数匹配并处理异常;
(2)throw可一次性跳出多层函数调用,直到最近一层的try语句,称为“栈展开”;
(3)catch捕获时是将异常对象与catch参数的进行 类型比较,而不是值比较,所以只要类型相同,就可以进入catch中处理。(例如throw抛出一个int类型的值,catch(int &i)就可以对其进行处理;或者throw抛出一个类对象,catch(Base& b)也可成功匹配)

所谓 “try”,就是 “尝试着执行一下”,如果有异常,则通过throw向外抛出,随后在外部通过catch捕获并处理异常。

0. 概述:

异常处理 将 问题的 “检测” 与 “解决” 过程分离开来。
程序的一部分负责检测问题的出现,然后解决该问题的任务传递给程序的另一部分。

检测环节无须知道处理模块的所有细节,处理模块也无须知道检测模块的细节。

这种机制允许在大型程序开发中 两个独立开发的部分能够在运行时 就出现的问题进行通信并做出相应的处理。

要想有效的使用异常处理,必须首先了解 当抛出异常时发生了什么、捕获异常时发生了什么,以及用来传递错误的对象的意义。

1. 抛出异常:

C++通过 “throw” 关键字 抛出一条表达式来触发一个异常。

throw的用法类似于return:
它通常作为 条件语句的一部分 或者 作为某个函数的最后一条语句,当throw执行时,跟在throw后面的语句将不再被执行,程序的控制权从throw转移到与之匹配的catch(catch可能是同一个函数中的局部catch,也可能位于调用链上的其他函数)。

try {throw expression;
}
catch(case 1) {}
catch(case 2) {}
catch(case 3) {}
...

1.1 throw的处理过程:(栈展开)

throw语句一般位于try语句块内,当throw抛出一个异常时,程序暂停当前函数的执行过程,并寻找与try语句块关联的catch语句(类似 switch…case…),
如果这一步没找到匹配的catch,且这一层的try语句外部又包含着另一层try,则在外层try中继续寻找匹配的catch,如果找不到,则退出当前函数,在当前函数的外层函数中继续寻找匹配的try与catch。

上述过程被称为“栈展开”(stack unwinding)过程。

栈展开 过程沿着嵌套函数的调用链不断查找,直到找到匹配的catch 子句为止;
或者一直没有找到匹配的catch,则退出主函数终止查找过程(调用标准库函数terminate)。

如果找到了一个匹配的catch子句,则程序进入该子句并执行其中的代码,执行完成后回到到这个 try…catch… 的最后一个catch之后的位置继续向下执行。

1.2 析构函数 与 异常:

当异常发生调用throw,后面的语句将不会被执行,退出作用域时,作用域的局部对象都将会被释放,对于类对象,退出作用域时将自动调用它的析构函数。

因此,如果析构函数中有抛出异常的流程,应该要在析构函数内部try捕获,并在析构函数内部得到处理。

1.3 异常对象:

在编译器的管理空间中,会维护一种“异常对象”,专门用于抛出异常时使用。
当发生异常时,编译器会用throw 抛出的表达式的值 对 “异常对象” 进行拷贝初始化,当异常处理完毕后,编译器会将“异常对象”销毁。

所以,基于 异常对象 的这种处理机制,对抛出异常的处理有几点限制:
① 如果throw抛出的表达式是类类型,则此类必须要有可访问的 拷贝构造函数和 析构函数;(因为对 异常对象 进行拷贝初始化 以及 释放 异常对象的时候需要调用)
② throw抛出的异常对象 不能是指向局部对象的指针;(因为throw退出作用域后,局部对象随之被释放掉,抛出指针到外层后将无法访问所指向的局部对象)
③ throw抛出的表达式 为 此表达式的 静态编译类型,如果抛出的是一个指向类对象的基类指针,则派生类部分将被截断,只有基类部分被抛出。

2. 捕获异常:

使用 catch子句 捕获异常。

2.1 catch的参数 与 函数参数 类似:

① 如果catch参数是非引用类型,则该参数是“异常对象”的一个副本,在catch语句内修改该参数实际上改变的是局部副本的值而非异常对象本身;
② 如果catch参数是引用类型,则该参数就是“异常对象”的一个别名。此引用同样适用于继承体系下的对象传递:基类类型引用可用于绑定派生类对象。

2.2 catch的匹配规则:

按照其出现顺序逐一匹配,但 允许类型转换、允许派生类向基类的类型转换、允许非常量向常量的类型转换,所以越是专门的catch越应该置于判断列表的前端。
(如果多个catch语句之间存在继承关系,应该将派生类放在前面,基类放在后面(继承链最低端的类放在前面,最顶端的类放在后面))

2.3 重新抛出:

在catch捕获异常并开始处理后,如果一个单独的catch不能完整的处理某个异常,则当前catch会 向调用链更上一层 重新抛出异常(rethrowing),这次是一个 空的 throw语句:
(空throw语句只能出现在catch语句中)

catch (my_error &eObj) {throw ;
}

如果catch的参数是引用类型,则 catch可将异常的内容修改后再向上层抛出。

2.4 捕获所有异常:

catch(...) 表示捕获所有类型的异常:

try {}
catch(...) {}

3. 函数try语句块 与 构造函数:

如果想要处理 构造函数初始阶段(初始值列表中发生的错误)的异常,写法是:

Bob::Bob(string i1) try : data(i1) {} catch(const bad_alloc &e) { handle_out_of_memory(e); }

注意 try 和 catch 的位置。

4. nonexcept异常说明:

明确指出某个函数不可能抛出异常,有助于简化调用该函数的代码。
如果我们能够提前知道这个函数没有throw机制,就不需要在调用该函数时编写任何异常处理的代码,否则如果有抛出异常而却没有进行捕获,则有可能造成程序调用 terminate 终止。

使用 nonexcept 表示函数不会抛出异常:

void recoup(int) nonexcept;  //不会抛出异常
void alloc(int);            //可能抛出异常

5. 异常类层次:

throw 可抛出 ① 自定义的异常对象,也可以抛出 ② 标准异常中的类型。

关于标准异常:

exception是所有异常的父类。

bad_cast     : 通过dynamic_cast 抛出;
runtime_error   : 运行时异常,包括 3个自子类:overflow_error / underflow_error / range_error
logic_error     : 逻辑错误,包括 4个子类:domain_error / invalid_argument / out_of_range / length_error
bad_alloc       : new失败时,会抛出bad_alloc;

exception 标准异常有个比较好用的功能是可以通过 e.what() 打印出出错的位置(获取一个标识异常的字符串),例如:

#include <iostream>
#include <deque>int main() {std::deque<int> mydeq;mydeq.push_back(1);mydeq.push_back(2);try {mydeq.at(2);   //访问越界,deque内部实现了throw抛出异常}catch(const std::exception &e) {std::cerr << "exception: " << e.what() << std::endl;}return 0;
}

输出结果:

exception: deque

std::cout(标准输出) 与 std::cerr(标准错误输出) 的区别:

二者都是默认向屏幕输出,不同点在于:
stdout是输出到磁盘文件,默认情况下stdout是行缓冲的,它的输出会先放在一个buffer里面,只有到换行的时候才会输出到屏幕上;
而stderr是无缓冲的,直接输出到屏幕上。

6. C++抛出异常 相比于 “return error”的好处:

https://bbs.csdn.net/topics/391839431

简言之:在多层函数调用时,throw使代码更简洁、异常处理更可靠:
① throw可以一下子跳出多层的函数调用,内层异常,本层次不是需要处处捕获;
② return error 只能返回一层,需要层层传递,或者使用全局变量记录错误码,如果一层忽略,就失去了处理的机会。

7. 实际开发中的抛出异常应用举例:

void CHttpQuery::_QueryCreateGroup(const string& strAppKey, Json::Value& post_json_obj, CHttpConn *pHttpConn) {try {}catch(std::runtime_error msg) {}
}void CImConn::OnRead() {try {}catch (CPduException& ex) {log_error();if(pPdu) {delete pPdu;pPdu = nullptr;}OnClose();}
}

简单使用举例:

#include <iostream>
using namespace std;int main() {int     ival  = 5;char     cchar = 'c';char     dchar = 'd';try {throw cchar;}catch(int) {cout << "int excpetion" << endl;}catch(char &c) {if(c == 'c') {cout << "cchar excpetion" << endl;}else if(c == 'd') {cout << "dchar excpetion" << endl;}}return 0;
}

输出结果:

cchar excpetion

C++的throw抛出异常机制相关推荐

  1. java throw抛出异常

    1.throws关键字通常被应用在声明方法时,用来指定可能抛出的异常.多个异常可以使用逗号隔开.当在主函数中调用该方法时,如果发生异常,就会将异常抛给指定异常对象.如下面例子所示: public cl ...

  2. c++ 使用throw抛出异常

    抛出异常(也称为抛弃异常)即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常.该语句的格式为: throw 表达式;     如果在try语句块的程序段中(包括 ...

  3. C#中throw抛出异常后,后续代码执行情况

    1.问题来源参见以下代码: public void add(int index, E element) {if(size >= elements.length) {throw new Runti ...

  4. throws和throw抛出异常的使用规则

    一直对java中的throws和throw不太理解.最近一直在查这两个方面的资料,算是能明白一点吧.如果我下面的观点哪有不对,希望指出来,我加以改进.         throw:(针对对象的做法) ...

  5. mysql 自定义抛出异常_C#自定义异常(throw抛出异常)

    虽然在 C# 语言中已经提供了很多异常处理类,但在实际编程中还是会遇到未涉及的一些异常处理. 例如想将数据的验证放置到异常处理中,即判断所输入的年龄必须为 18〜45,此时需要自定义异常类来实现. 自 ...

  6. java学习(126):throw向上抛出异常

    //throw抛出异常 import java.util.Scanner; public class test66{static class A {void d() throws Exception ...

  7. 请不要将抛出异常作为业务逻辑使用!!!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:myfor www.cnblogs.com/myfor/p/ ...

  8. C# Note34: 异常机制相关小点

    1.使用throw和throw ex抛出异常的区别 通常,我们使用try/catch/finally语句块来捕获异常,那么在抛出异常的时候,使用throw和throw ex有什么区别呢? 假如,按顺序 ...

  9. 异常(try...catch...finally、throws、throw)

    当出现程序无法控制的外部环境(文件不存在,文件内容损坏,网络不可用等)问题时,java就会用异常对象来描述. Java中用2种方法处理异常: 1. 在发生异常的地方直接处理. 2. 将异常抛给调用者, ...

最新文章

  1. 今天我们来聊一聊 Spring 中的线程安全性
  2. 亚马逊AWS在线系列讲座——基于AWS云平台的高可用应用设计
  3. go语言判断手机号归属地
  4. catia v5法矢数据软件_catia介绍
  5. android bitmap string,Android Bitmap到Base64字符串(Android Bitmap to Base64 String)
  6. 基类数组存放派生类_永远不要将派生类数组赋值给基类类型指针
  7. WebGIS中解决使用Lucene进行兴趣点搜索排序的两种思路
  8. 用Navicat连接mysql报错:2003-Can't connect to MySql server on '10.100.0.109'(10039)
  9. crfpp python
  10. UC伯克利超酷研究:舞痴和舞王之间,只差一个神经网络
  11. oracle @id@,修改oracle用户id
  12. 管理感悟:出了事故,关键是想想自己哪里能改进
  13. 403. Frog Jump
  14. 软件测试——测试用例和测试设计方法
  15. Android BT STACK BTU 和 HCI之间的消息传递
  16. c语言题库字母顺序,C语言题库(带答案)-排版-
  17. 【python 监控报警】错误日志监控并钉钉报警
  18. 计算机组装图纸手画,原神玩家为造家园能有多拼?工科大佬直接画出图纸,成品效果惊人...
  19. iOS内存管控实战(上)—原理篇
  20. ctf你好 1.1:灵魂拷问

热门文章

  1. Windows无法连接到System Event Notification Service服务问题解决
  2. 线性联立方程的高斯赛德尔迭代(Gauss-Seidel iteration)(python,数值积分)
  3. iis(虚拟服务器),iis虚拟主机控制面板(虚拟主机专用控制面板)
  4. C语言谭浩强第5版章节编程题
  5. 存放素数数组JAVA_java – 返回素数数组
  6. Java8读文件的方法
  7. 『Halcon』基于Halcon的印刷图像质量检测系统
  8. 解决selenium与chrome版本不匹配问题
  9. Docker入门书籍
  10. sql 当前时间加一天