C++的throw抛出异常机制
关于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抛出异常机制相关推荐
- java throw抛出异常
1.throws关键字通常被应用在声明方法时,用来指定可能抛出的异常.多个异常可以使用逗号隔开.当在主函数中调用该方法时,如果发生异常,就会将异常抛给指定异常对象.如下面例子所示: public cl ...
- c++ 使用throw抛出异常
抛出异常(也称为抛弃异常)即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常.该语句的格式为: throw 表达式; 如果在try语句块的程序段中(包括 ...
- C#中throw抛出异常后,后续代码执行情况
1.问题来源参见以下代码: public void add(int index, E element) {if(size >= elements.length) {throw new Runti ...
- throws和throw抛出异常的使用规则
一直对java中的throws和throw不太理解.最近一直在查这两个方面的资料,算是能明白一点吧.如果我下面的观点哪有不对,希望指出来,我加以改进. throw:(针对对象的做法) ...
- mysql 自定义抛出异常_C#自定义异常(throw抛出异常)
虽然在 C# 语言中已经提供了很多异常处理类,但在实际编程中还是会遇到未涉及的一些异常处理. 例如想将数据的验证放置到异常处理中,即判断所输入的年龄必须为 18〜45,此时需要自定义异常类来实现. 自 ...
- java学习(126):throw向上抛出异常
//throw抛出异常 import java.util.Scanner; public class test66{static class A {void d() throws Exception ...
- 请不要将抛出异常作为业务逻辑使用!!!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:myfor www.cnblogs.com/myfor/p/ ...
- C# Note34: 异常机制相关小点
1.使用throw和throw ex抛出异常的区别 通常,我们使用try/catch/finally语句块来捕获异常,那么在抛出异常的时候,使用throw和throw ex有什么区别呢? 假如,按顺序 ...
- 异常(try...catch...finally、throws、throw)
当出现程序无法控制的外部环境(文件不存在,文件内容损坏,网络不可用等)问题时,java就会用异常对象来描述. Java中用2种方法处理异常: 1. 在发生异常的地方直接处理. 2. 将异常抛给调用者, ...
最新文章
- 今天我们来聊一聊 Spring 中的线程安全性
- 亚马逊AWS在线系列讲座——基于AWS云平台的高可用应用设计
- go语言判断手机号归属地
- catia v5法矢数据软件_catia介绍
- android bitmap string,Android Bitmap到Base64字符串(Android Bitmap to Base64 String)
- 基类数组存放派生类_永远不要将派生类数组赋值给基类类型指针
- WebGIS中解决使用Lucene进行兴趣点搜索排序的两种思路
- 用Navicat连接mysql报错:2003-Can't connect to MySql server on '10.100.0.109'(10039)
- crfpp python
- UC伯克利超酷研究:舞痴和舞王之间,只差一个神经网络
- oracle @id@,修改oracle用户id
- 管理感悟:出了事故,关键是想想自己哪里能改进
- 403. Frog Jump
- 软件测试——测试用例和测试设计方法
- Android BT STACK BTU 和 HCI之间的消息传递
- c语言题库字母顺序,C语言题库(带答案)-排版-
- 【python 监控报警】错误日志监控并钉钉报警
- 计算机组装图纸手画,原神玩家为造家园能有多拼?工科大佬直接画出图纸,成品效果惊人...
- iOS内存管控实战(上)—原理篇
- ctf你好 1.1:灵魂拷问
热门文章
- Windows无法连接到System Event Notification Service服务问题解决
- 线性联立方程的高斯赛德尔迭代(Gauss-Seidel iteration)(python,数值积分)
- iis(虚拟服务器),iis虚拟主机控制面板(虚拟主机专用控制面板)
- C语言谭浩强第5版章节编程题
- 存放素数数组JAVA_java – 返回素数数组
- Java8读文件的方法
- 『Halcon』基于Halcon的印刷图像质量检测系统
- 解决selenium与chrome版本不匹配问题
- Docker入门书籍
- sql 当前时间加一天