C++中try_catch_throw的做异常处理

选择异常处理的编程方法的具体原因如下:

、 把错误处理和真正的工作分开来;

、 代码更易组织,更清晰,复杂的工作任务更容易实现;

、 毫无疑问,更安全了,不至于由于一些小的疏忽而使程序意外崩溃了;

、 由于C++中的try catch可以分层嵌套,所以它提供了一种方法使得程序的控制流可以安全的跳转到上层(或者上上层)的错误处理模块中去。(不同于return语句,异常处理的控制流是可以安全地跨越一个或多个函数 )。

、 还有一个重要的原因就是,由于目前需要开发的软件产品总是变得越来越复杂、越来越庞大,如果系统中没有一个可靠的异常处理模型,那必定是一件十分糟糕的局面。

异常处理仅仅通过类型而不是通过值来匹配的,否则又回到了传统的错误处理技术上去了,所以catch块的参数可以没有参数名称,只需要参数类型,除非要使用那个参数。

其中关键字try表示定义一个受到监控、受到保护的程序代码块;关键字catch与try遥相呼应,定义当try block(受监控的程序块)出现异常时,错误处理的程序模块,并且每个catch block都带一个参数(类似于函数定义时的数那样),这个参数的数据类型用于异常对象的数据类型进行匹配;而throw则是检测到一个异常错误发生后向外抛出一个异常事件,通知对应的catch程序块执行对应的错误处理。

、还是给一个例子吧!如下:

int main()

{

cout << "In main." << endl;

//定义一个try block,它是用一对花括号{}所括起来的块作用域的代码块

try

{

cout << "在 try block 中, 准备抛出一个异常." << endl;

//由于在try block中的代码是受到监控保护的,所以抛出异常后,程序的

//控制流便转到随后的catch block中

throw 1;

cout << "在 try block 中, 由于前面抛出了一个异常,因此这里的代码是不会得以执行到的" << endl;

}

//这里必须相对应地,至少定义一个catch block,同样它也是用花括号括起来的

catch( int& value )

{

cout << "在 catch block 中, 处理异常错误。异常对象value的值为:"<< value << endl;

}

cout << "Back in main. Execution resumes here." << endl;

return 0;

}

、语法很简单吧!的确如此。另外一个try block可以有多个对应的catch block,可为什么要多个catch block 呢?这是因为每个catch block匹配一种类型的异常错误对象的处理,多个catch block呢就可以针对不同的异常错误类型分别处理。毕竟异常错误也是分级别的呀!有致命的、有一般的、有警告的,甚至还有的只是事件通知。例子如下:

int main()

{

try

{

cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl;

throw 1;

cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl;

throw 0.5;

}

catch( int& value )

{

cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;

}

catch( double& d_value )

{

cout << "在 catch block 中, double数据类型处理异常错误。"<< endl;

}

return 0;

}

、一个函数中可以有多个trycatch结构块,例子如下:

int main()

{

try

{

cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl;

throw 1;

}

catch( int& value )

{

cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;

}

//这里是二个trycatch结构块,当然也可以有第三、第四个,甚至更多

try

{

cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl;

throw 0.5;

}

catch( double& d_value )

{

cout << "在 catch block 中, double数据类型处理异常错误。"<< endl;

}

return 0;

}

4、上面提到一个try block可以有多个对应的catch block,这样便于不同的异常错误分类处理,其实这只是异常错误分类处理的方法之一(暂且把它叫做横向展开的吧!)。另外还有一种就是纵向的,也即是分层的、trycatch块是可以嵌套的,当在低层的trycatch结构块中不能匹配到相同类型的catch block时,它就会到上层的trycatch块中去寻找匹配到正确的catch block异常处理模块。例程如下:

int main()

{

try

{

//这里是嵌套的trycatch结构块

try

{

cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl;

throw 1;

}

catch( int& value )

{

cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;

}

cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl;

throw 0.5;

}

catch( double& d_value )

{

cout << "在 catch block 中, double数据类型处理异常错误。"<< endl;

}

return 0;

}

、讲到是trycatch块是可以嵌套分层的,并且通过异常对象的数据类型来进行匹配,以找到正确的catch block异常错误处理代码。这里就不得不详细叙述一下通过异常对象的数据类型来进行匹配找到正确的catch block的过程。

) 首先在抛出异常的trycatch块中查找catch block,按顺序先是与第一个catch block块匹配,如果抛出的异常对象的数据类型与catch block中传入的异常对象的临时变量(就是catch语句后面参数)的数据类型完全相同,或是它的子类型对象,则匹配成功,进入到catch block中执行;否则到二步;

) 如果有二个或更多的catch block,则继续查找匹配第二个、第三个,乃至最后一个catch block,如匹配成功,则进入到对应的catch block中执行;否则到三步;

) 返回到上一级的trycatch块中,按规则继续查找对应的catch block。如果找到,进入到对应的catch block中执行;否则到四步;

) 再到上上级的trycatch块中,如此不断递归,直到匹配到顶级的trycatch块中的最后一个catch block,如果找到,进入到对应的catch block中执行;否则程序将会执行terminate()退出。

另外分层嵌套的trycatch块是可以跨越函数作用域的,例程如下:

void Func() throw()

{

//这里实际上也是嵌套在里层的trycatch结构块

try

{

cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl;

//由于这个trycatch块中不能找到匹配的catch block,所以

//它会继续查找到调用这个函数的上层函数的trycatch块。

throw 1;

}

catch( float& value )

{

cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;

}

}

int main()

{

try

{

Func();

cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl;

throw 0.5;

}

catch( double& d_value )

{

cout << "在 catch block 中, double数据类型处理异常错误。"<< endl;

}

catch( int& value )

{

//这个例子中,Func()函数中抛出的异常会在此被处理

cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;

}

return 0;

}

、刚才提到,嵌套的trycatch块是可以跨越函数作用域的,其实这里面还有另外一层涵义,就是抛出异常对象的函数中并不一定必须存在 trycatch块,它可以是调用这个函数的上层函数中存在trycatch块,这样这个函数的代码也同样是受保护、受监控的代码;当然即便是上层调用函数不存在trycatch块,也只是不能找到处理这类异常对象错误处理的catch block而已,例程如下:

void Func() throw()

{

//这里实际上也是嵌套在里层的trycatch结构块

//由于这个函数中是没有trycatch块的,所以它会查找到调用这个函数的上

//层函数的trycatch块中。

throw 1;

}

int main()

{

try

{

//调用函数,注意这个函数里面抛出一个异常对象

Func();

cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl;

throw 0.5;

}

catch( double& d_value )

{

cout << "在 catch block 中, double数据类型处理异常错误。"<< endl;

}

catch( int& value )

{

//这个例子中,Func()函数中抛出的异常会在此被处理

cout << "在 catch block 中, int数据类型处理异常错误。"<< endl;

}

//如果这里调用这个函数,那么由于main()已经是调用栈的顶层函数,因此不能找到对应的catch block,所以程序会执行terminate()退出。

Func();

// [特别提示]:在C++标准中规定,可以在程序任何地方throw一个异常对象,并不要求一定只能是在受到try block监控保护的作用域中才能抛出异常,但如果在程序中出现了抛出的找不到对应catch block的异常对象时,C++标准中规定要求系统必须执行terminate()来终止程序。 因此这个例程是可以编译通过的,但运行时却会异常终止。这往往给软件系统带来了不安全性。与此形成对比的是java中提供的异常处理模型却是不允许出现这样的找不到对应catch block的异常对象,它在编译时就给出错误提示,所以java中提供的异常处理模型往往比C++要更完善,后面的章节会进一步对这两种异常处理模型进行一个详细的分析比较。

return 0;

}

1、基础介绍

try

{

//程序中抛出异常

throw value;

}

catch(valuetype v)

{

//例外处理程序段

}

语法小结:throw抛出值,catch接受,当然,throw必须在"try语句块"中才有效。

2、深入throw:

(i)、程序接受到throw语句后就会自动调用析构器,把该域(try后的括号内)对象clean up,然后再进

入catch语句(如果在循环体中就退出循环)。

这种机制会引起一些致命的错误,比如,当"类"有指针成员变量时(又是指针!),在 "类的构建器

"中的throw语句引起的退出,会导致这个指针所指向的对象没有被析构。这里很基础,就不深入了,提

示一下,把指针改为类就行了,比如模板类来代替指针,在模板类的内部设置一个析构函数。

(ii)、语句"throw;"抛出一个无法被捕获的异常,即使是catch(...)也不能捕捉到,这时进入终止函数

,见下catch。

3、深入catch:

一般的catch出现的形式是:

try{}

catch(except1&){}

catch(except2&){}

catch(...){} //接受所有异常

一般都写成引用(except1&),原因很简单,效率。

问题a:抛出异常,但是catch不到异常怎么办?(注意没有java类似的finally语句)

在catch没有捕获到匹配的异常的时候,会调用默认的终止函数。可以调用set_terminate()来设置终止函数,参数是一个函数指针,类型是:void (*terminate)()。

到这里,可以题个问题:"没有try-catch,直接在程序中"throw;",会怎么样?"

其他一些技巧:

4、try一个函数体,形式如下

void fun(type1,type2) try----try放在函数体后

{

函数定义

}

catch(typeX){}

这个用法的效果就相当于:

void fun()

{

try{函数定义}

}

5、throw一个函数体,形式如下:

void fun (); // 能抛出任何类型的异常

void fun () throw(except1,except2,except3)

// 后面括号里面是一个异常参数表,本例中只能抛出这3中异常

void fun () throw()   // 参数表为空,不能抛出异常

问题b:假设fun()中抛出了一个不在"异常参数表"中的异常,会怎么样?

答:调用set_terminate()中设定的终止函数。然而,这只是表面现象,实际上是调用默认的unexpected()函数,然而这个默认的unexpected()调用了set_terminate()中设定的终止函数。可以用set_unexpected()来设置unexpected,就像set_terminate()一样的用法,但是在设定了新的"unexpected()"之后,就不会再调用set_terminater中设定的终止函数了。

这个语法是很有用的,因为在用别人的代码时,不知道哪个地方会调用什么函数又会抛出什么异常,用一个异常参数表在申明时限制一下,很实用。

天学通C++》一书

抛出异常(也称为抛弃异常)即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:

throw 表达式;

如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。由于C++使用数据类型来区分不同的异常,因此在判断异常时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。

的异常。该范例将上述除数为0的异常可以用try/catch语句来捕获异常,并使用throw语句来抛出异常,从而实现异常处理

代码清单20-2

#include<iostream>

#include<stdlib.h>

using namespace std;

double fuc(double x, double y) //定义函数

{

if(y==0)

{

throw y; //除数为0,抛出异常

}

return x/y; //否则返回两个数的商

}

void main()

{

double res;

条有可能出现异常的语句

{

res = fuc(2, 3);

cout << "The result of x/y is : " <<res<<endl;

res = fuc(4, 0); //出现异常

}

catch (double) //处理异常,捕获到异常后执行此代码

{

cerr << "error of dividing zero.\n";

exit(1); //异常退出程序

}

}

结果:

The result of x/y is:0.666667

Error of dividing zero.

注意:一般来说,throw语句通常与try- catch或try-finally语句一起使用,可以使用throw语句显式引发异常。

、基础介绍

try

{

//程序中抛出异常

throw value;

}

catch(valuetype v)

{

//例外处理程序段

}

语法小结:throw抛出值,catch接受,当然,throw必须在"try语句块"中才有效。

、深入throw:

(i)、程序接受到throw语句后就会自动调用析构器,把该域(try后的括号内)对象clean up,然后再进

入catch语句(如果在循环体中就退出循环)。

这种机制会引起一些致命的错误,比如,当"类"有指针成员变量时(又是指针!),在 "类的构建器

"中的throw语句引起的退出,会导致这个指针所指向的对象没有被析构。这里很基础,就不深入了,提

示一下,把指针改为类就行了,比如模板类来代替指针,在模板类的内部设置一个析构函数。

(ii)、语句"throw;"抛出一个无法被捕获的异常,即使是catch(...)也不能捕捉到,这时进入终止函数

,见下catch。

、深入catch:

一般的catch出现的形式是:

try{}

catch(except1&){}

catch(except2&){}

catch(...){} //接受所有异常

一般都写成引用(except1&),原因很简单,效率。

问题a:抛出异常,但是catch不到异常怎么办?(注意没有java类似的finally语句

在catch没有捕获到匹配的异常的时候,会调用默认的终止函数。可以调用set_terminate()来设置终止函数,参数是一个函数指针,类型是:void (*terminate)()。

到这里,可以题个问题:"没有try-catch,直接在程序中"throw;",会怎么样?"

其他一些技巧:

、try一个函数体,形式如下

void fun(type1,type2) try----try放在函数体后

{

函数定义

}

catch(typeX){}

这个用法的效果就相当于:

void fun()

{

try{函数定义}

}

、throw一个函数体,形式如下:

void fun (); // 能抛出任何类型的异常

void fun () throw(except1,except2,except3)

中异常

void fun () throw()   // 参数表为空,不能抛出异常

问题b:假设fun()中抛出了一个不在"异常参数表"中的异常,会怎么样?

答:调用set_terminate()中设定的终止函数。然而,这只是表面现象,实际上是调用默认的unexpected()函数,然而这个默认的unexpected()调用了set_terminate()中设定的终止函数。可以用set_unexpected()来设置unexpected,就像set_terminate()一样的用法,但是在设定了新的"unexpected()"之后,就不会再调用set_terminater中设定的终止函数了。

这个语法是很有用的,因为在用别人的代码时,不知道哪个地方会调用什么函数又会抛出什么异常,用一个异常参数表在申明时限制一下,很实用。

C++中try_catch_throw的做异常处理的更多相关文章

  1. Yii中的错误及异常处理

    Yii中的错误及异常处理 Yii已经默认已经在CApplication上实现了异常和错误的接管,这是通过php的set_exception_handler, set_error_handler实现的. ...

  2. JAVA项目中的常用的异常处理情况总结

    可能遇见的异常或错误: 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的.例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略. 运行时异常: ...

  3. bootstrap中table页面做省市区级联效果(级联库见前面级联编辑)(非select下拉框)

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  4. 如何在程序中调用Caffe做图像分类

    Caffe是目前深度学习比较优秀好用的一个开源库,采样c++和CUDA实现,具有速度快,模型定义方便等优点.学习了几天过后,发现也有一个不方便的地方,就是在我的程序中调用Caffe做图像分类没有直接的 ...

  5. 【spring】在spring cloud项目中使用@ControllerAdvice做自定义异常拦截,无效 解决原因

    之前在spring boot服务中使用@ControllerAdvice做自定义异常拦截,完全没有问题!!! GitHub源码地址: 但是现在在spring cloud中使用@ControllerAd ...

  6. 解决docker中使用nginx做负载均衡时并发过高时的一些问题

    # 解决docker中使用nginx做负载均衡时并发过高时的一些问题 1.问题产生原因: 由于通过nginx作为负载均衡服务,在访问并发数量达到一定量级时jmeter报错. nginx日志关键信息:a ...

  7. 定时任务-----Springboot中使用Scheduled做定时任务----http://www.cnblogs.com/lirenqing/p/6596557.html

    Springboot中使用Scheduled做定时任务---http://www.cnblogs.com/lirenqing/p/6596557.html 已经验证的方案: pom文件加入依赖 < ...

  8. 【异常处理】Java异常如何做异常处理

    类似SpringMVC项目的异常处理可以这样做: 整个项目创建全局的: 1.一个自定义异常如OneException和错误码,统一封装所有异常. 2.一个返回实体类ResponseEntity,包含返 ...

  9. 关于JAVA项目中的常用的异常处理情况

         Java异常处理的几个原则如下.       1)不要丢弃异常,捕获异常后需要进行相关处理.如果用户觉得不能很好地处理该异常,就让它继续传播,传到别的地方去处理,或者把一个低级的异常转换成应 ...

随机推荐

  1. javascript 实现图片轮播和点击切换功能

    图片轮播是网页上一个比较常见的功能,下面我们来实现他吧 原理很简单: 1:固定的区域,所有的图片重叠,一次只能显示一张图片 2:通过改变图片的zIndex属性改变显示的图片,就可以达到切换的效果了 & ...

  2. How To Install Nginx on Ubuntu 16.04 zz

    Introduction Nginx is one of the most popular web servers in the world and is responsible for hostin ...

  3. windows命令启动mysql

    找到mysql的安装位置,进入bin目录 dos输入  mysql -h localhost -uroot -p   ,在输入密码

  4. 解决Windows10与Ubuntu系统时间不一致问题

    前言: 安装完windows与Ubuntu双系统之后会发现windows与Ubuntu时间不一致.这是硬件时间都一样的情况下,Ubuntu使用的是UST,Windows使用的是CST.要解决该问题就要 ...

  5. Linux NFS服务器的简明配置6.8

    Linux NFS服务器的简明配置6.8   Linux NFS服务器的简明配置 一.NFS服务简介 NFS 是Network File System的缩写,即网络文件系统.一种使用于分散式文件系统的 ...

  6. 对TDD原则的理解

    1,在编写好失败的单元测试之前,不要编写任何产品代码    如果不先写测试,那么各个函数就会耦合在一起,最后变得无法测试    如果后写测试,你也许能对大块大块的代码进行测试,但是无法对每个函数进行测 ...

  7. 洛谷 P2680 运输计划-二分+树上差分(边权覆盖)

    P2680 运输计划 题目背景 公元 20442044 年,人类进入了宇宙纪元. 题目描述 公元20442044 年,人类进入了宇宙纪元. L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条 ...

  8. php过滤表单输入的emoji表情

    1.过滤emoji表情的原因 在我们的项目开发中,emoji表情是个麻烦的东西,即使我们可以能存储,也不一定能完美显示,因为它的更新速度很快:在iOS以外的平台上,例如PC或者android.如果你需 ...

  9. 最短路&生成树&二分图匹配&费用流问题

    最短路 题意理解,建图 https://vjudge.net/problem/UVALive-4128 飞机票+行程建图 https://vjudge.net/problem/UVALive-3561 ...

  10. RabbitMQ (十二) 消息确认机制 - 发布者确认

    消费者确认解决的问题是确认消息是否被消费者"成功消费". 它有个前提条件,那就是生产者发布的消息已经"成功"发送出去了. 因此还需要一个机制来告诉生产者,你发送 ...