C++中的异常处理

1、C++异常处理

(1)C++内置了异常处理的语法元素,try...catch...,这是两个新的关键字在C++中

@1:try语句代码块中用来处理正常代码逻辑

@2:catch语句代码块中用来处理异常情况

@3:try语句中的异常由对应的catch语句进行处理

try

{

douuble r = divide(1, 0);

}

catch(...)

{

cout << "Divide by zero..." << endl;

}

@4:try语句代码块中用来处理可能发生异常的正常逻辑代码,当try代码块中的代码发生了异常,就会抛出异常,catch语句就会捕捉到这个异常,进入到catch语句中进行处理这个异常。

(2)那么try代码块中的语句中是如何抛出异常的呢?

@1:C++通过throw关键字抛出异常信息

如:使用throw语句进行异常抛出

double divide(double a, double b)

{

const double delta = 0.000000000001;

double ret = 0;

if ( !((-delta < b) && (b < delta)) )

{

ret = a / b;

}

else

{

throw 0; //产生除0异常,这里是0这个字面常量来代码了当前的异常元素,异常元素可以是字符串,可以是对象,可以是一个值等等

//当程序执行到throw时,就会返回到调用这个divide函数的调用点,try就会将这个异常元素转给catch语句,catch语句块就会抓住这个异常元素

}

return ret;

}

2、C++异常处理分析

(1)throw抛出的异常必须被catch处理

@1:当前函数能够处理异常,程序继续往下执行

@2:当前函数如果无法处理收到的异常,则函数停止执行,并返回

(2)未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。

比如:如果function1函数调用了function2函数,function2函数调用了function3函数,在function3函数中执行执行,并抛出了异常,也就是throw了,那么这个异常就会先看function3这个函数有没有能力处理这个异常(也就是在没在try中,有没有对应的catch处理的异常类型),如果有就进行异常处理了,如果没有function3这个函数就会立即停止执行,并代码着异常返回给function2调用function3函数的调用点,如果function2没有进行处理(没有在try中,也没有catch处理的相关类型),function2就会立即停止执行,带着异常返回给function1函数调用function2函数的调用点。如果都没有对throw扔出的这个异常元素进行异常处理的话(函数调用没在try中也没有对应的catch异常处理的相关类型),整个程序就会放弃执行

例:throw抛出异常,对异常进行处理,try...catch

#include <iostream>

#include <string>

using namespace std;

double divide(double a, double b)

{

const double delta = 0.00000000001;

double ret = 0;

if ( !((-delta < b) && (b < delta)) )

{

ret = a / b;

}

else

{

throw 0; //产生除0异常,这里是0这个int类型的值来代码了当前的异常元素,异常元素可以是字符串,可以是对象,可以是一个值等等

//当程序执行到throw时,就会返回到调用这个divide函数的调用点,try就会将这个异常元素转给catch语句,catch语句块就会抓住这个异常元素 //如果没有对应的catch对这个异常进行处理,程序将会放弃执行

}

return ret;

}

int main(int argc, char *argv[])

{

double num = 0;

try

{

num = divide(1, 1); //执行到divide函数throw语句时,就会返回throw语句后面的异常元素给这个try,这个try将异常元素给了catch,catch对这个异常进行处理。

//如果没有对异常进行处理的操作,但你throw还抛出了异常,程序会停止运行

cout << "num = " << num << endl;

}

catch(...)

{

cout << "Divide by zero ...." << endl;

}

return 0;

}

(3)同一个try语句可以跟上多个catch语句

@1:catch语句可以定义具体处理的异常类型

@2:不同类型的异常由不同的catch语句负责处理

@3:try语句中可以抛出任何类型的异常

@4:catch(...)用于处理所有类型的异常,并且这个catch(...)里面是3个点的catch语句块只能放在最后catch处理的情况,当有其他catch存在时。

@5:任何异常都只能被捕获(catch)一次

(4)异常处理的匹配原则

try

{

throw 1;

}

catch (Type1 t1)

{

}

catch (Type2 t2)

{

}

catch (TypeN tn)

{

}

catch (...) //这个catch,处理任何类型的异常,当有其他catch存在时,只能作为最后的catch处理情况

{

}

异常抛出后,至上而下严格的匹配每一个catch语句处理的类型。异常处理匹配时,不进行任何的类型转换。所以是严格匹配的。

如果当前抛出异常的函数没有对这个异常处理,就会沿着当前函数的调用栈,顺序的返回,直到被处理,如果都没有进行这个异常处理,程序就会停止执行

例:一个try抛出异常,多个catch进行异常类型匹配处理异常的情况

#include <iostream>

#include <string>

/*

*一个try中抛出异常,多个catch进行匹配,

*匹配原则是严格的类型匹配,不会进行类型的转换,

*至上而下的进行匹配catch中的类型,catch(...)只能放在对try中抛出异常的处理最后

*/

using namespace std;

void Demo1()

{

try

{

throw 0; //直接抛出异常,这个是int类型的

}

catch (char c)

{

cout << "catch (char c)" << endl;

}

catch (double d)

{

cout << "catch (double d)" << endl;

}

catch (string s)

{

cout << "catch (string s)" << endl;

}

catch (int i)

{

cout << "catch (int i)" << endl;

}

catch (...)

{

cout << "catch (...)" << endl;

}

}

void Demo2()

{

try

{

throw "haha"; //这个const char* 类型的

}

catch (char *s)

{

cout << "catch (char *s)" << endl;

}

catch (string ss)

{

cout << "catch (string ss)" << endl;

}

catch (const char * cs)

{

cout << "catch (const char * cs)" << endl;

}

}

int main(int argc, char *argv[])

{

Demo1();

try

{

Demo2();

}

catch (...)

{

cout << "catch (...)" << endl;

}

return 0;

}

最后的执行结果,会打印catch (int i)和catch (...)

3、catch语句块中也可以抛出异常

try

{

func();

}

catch(int i)

{

throw i; //将捕获到的异常重新抛出。

}

catch(...)

{

throw; //将捕获到的异常重新抛出

}

catch中抛出的异常需要外层的try...catch...捕获。

(1)C++中之所以支持catch语句块中抛出异常,是因为我们在工程开发中,会经常的使用第三方库进行开发,如果第三方库中的func函数在使用时有可能会抛出异常,并且抛出的异常是-1,-2,-3等int类型异常,每一个异常元素对应的意思可以看第三方库中的文档来知道,但是我们在开发中,如果真遇到了第三方库抛出了异常,但是我们确无法直观的直接从这几个-1,-2,-3异常元素来知道每一个异常元素对应的是什么情况,只能去查第三方库提供的文档来知道,这是很浪费时间的,所以我们为了开发效率,所以我们将会将第三方库抛出的异常,进行统一的封装,也就是将func函数在我们自己写的Myfunc函数中调用,在Myfunc函数中,我们将第三方库func函数中可能抛出的异常元素进行重解释在抛出,这样我们就可以在工程开发中直接处理Myfunc这个函数抛出的重解释了第三方库func函数中抛出的异常。方便处理。

例:工程中在catch中抛出异常的用法,用于将第三方库中提供的函数抛出的异常进行重解释。

#include <iostream>

#include <string>

using namespace std;

/*

假设:func函数是第三方库中提供的函数,这个函数我们是无法修改的,因为我们得不到源码一般情况下

一般情况下,我们用的是第三方库提供的动态链接库。

func函数会抛出异常:

-1: 表示参数异常了

-2: 运行异常

-3: 超时异常

当这个func函数抛出异常的时候,我们无法直观的从它抛出的异常来知道究竟是发生了什么情况,只能去查阅第三方的文档。

所以为了方便,也为了架构的考虑,因为我们开发时,一般还有自己的私有库,所以我们就会对这个第三方库函数的异常进行重解释处理。

处理方法,就是我们自己写一个MyFunc函数,这个函数中调用了这个第三方库func函数,对这个func函数可能会抛出的异常进行重解释处理。

这样,我们的工程在使用func函数出现异常的时候,就只是针对于Myfunc我们自己写的这个函数的异常,同时异常的意思也被我们重解释的更清晰了

*/

void func(int i)

{

if (i < 10)

{

throw -1;

}

else if (i == 11)

{

throw -2;

}

else if (i > 100)

{

throw -3;

}

}

void MyFunc(int i) //自己提供的函数,完成和func一样的功能,只是为了重解释一下第三方库func函数抛出的异常

{

try

{

func(i);

}

catch (int i)

{

switch (i) //对第三方func函数抛出的异常进行重解释。

{

case -1:

throw "Invalid Exception";

break;

case -2:

throw "Run Exception";

break;

case -3:

throw "Timeout Exceptin";

break;

}

}

}

int main(void)

{

try

{

MyFunc(101);

}

catch (const char *cs)

{

cout << "Exception Info: " << cs << endl;

}

return 0;

}

4、异常的类型可以是自定义的类类型

(1)对于类类型的匹配依然是至上而下的严格匹配

(2)赋值兼容性原则在异常匹配中依然适用(子类的异常对象,可以被父类的catch语句块抓住)

(3)所以一般而言:

@1:匹配子类异常的catch放在上部

@2:匹配父类异常的catch放在下部

(5)在工程中会定义一系列的异常类

@1:每个类代表工程中可能出现的一种异常类型

@2:代码复用 时可能需要重解释不同的异常类

@3:在定义catch语句块时如果使用的异常是类对象,那么推荐使用引用作为参数,因为这样可以避开拷贝构造,提高程序效率

例:用异常类对异常进行重解释

#include <iostream>

#include <string>

using namespace std;

/*

工程中一般会常使用异常类,自定义一个异常类,来表示出现异常时的详细信息

*/

class Base

{

};

class Exception : public Base //继承了Base,所以catch接受这个类抛出的异常时,catch接受这个父类的异常处理要放到后面

{

private:

int m_id; //异常的ID号,也就是第三方库func函数中抛出异常的异常元素号。

string m_desc; //异常的信息描述

public:

Exception(int id, string desc)

{

m_id = id;

m_desc = desc;

}

int id() const

{

return m_id;

}

string description() const

{

return m_desc;

}

};

/*

假设:func函数是第三方库中提供的函数,这个函数我们是无法修改的,因为我们得不到源码一般情况下

一般情况下,我们用的是第三方库提供的动态链接库。

func函数会抛出异常:

-1: 表示参数异常了

-2: 运行异常

-3: 超时异常

当这个func函数抛出异常的时候,我们无法直观的从它抛出的异常来知道究竟是发生了什么情况,只能去查阅第三方的文档。

所以为了方便,也为了架构的考虑,因为我们开发时,一般还有自己的私有库,所以我们就会对这个第三方库函数的异常进行重解释处理。

处理方法,就是我们自己写一个MyFunc函数,这个函数中调用了这个第三方库func函数,对这个func函数可能会抛出的异常进行重解释处理。

这样,我们的工程在使用func函数出现异常的时候,就只是针对于Myfunc我们自己写的这个函数的异常,同时异常的意思也被我们重解释的更清晰了

*/

void func(int i)

{

if (i < 10)

{

throw -1;

}

else if (i == 11)

{

throw -2;

}

else if (i > 100)

{

throw -3;

}

}

void MyFunc(int i) //自己提供的函数,完成和func一样的功能,只是为了重解释一下第三方库func函数抛出的异常

{

try

{

func(i);

}

catch (int i)

{

switch (i) //对第三方func函数抛出的异常进行重解释。

{

case -1:

throw Exception(-1, "Invalid Exception");

break;

case -2:

throw Exception(-2, "Run Exception");

break;

case -3:

throw Exception(-3, "Timeout Exceptin");

break;

}

}

}

int main(void)

{

try

{

MyFunc(111);

}

catch (const Exception& e)

{

cout << "Exception Info: " << endl;

cout << "ID: " << e.id() << endl;

cout << "Description: " << e.description() << endl;

}

catch (const Base& e) //父类的接受异常要放到后面,因为赋值兼容性原则,如果这个接受异常放在了前面,那么抛出的异常就会被父类接受到了

{

cout << "catch (const Base& e)" << endl;

}

return 0;

}

6、C++标准库中提供了实用异常类族,使用时要包含<stdexcept>这个头文件,并且要声明使用的命名空间是std

(1)标准库中的异常都是从exception顶层父类派生的

(2)exception类有两个主要分支,在于异常的类型是不一样的

@1:logic_error

常用于程序中的可避免逻辑错误,(out_of_range("可以有参数,字符串参数,只是哪个函数发生的异常");数组访问越界,参数错误等)

@2:runtime_error

常用于程序中无法避免的恶性错误()

时间: 2024-11-02 11:27:51

C++中的异常处理的相关文章

项目中java异常处理

一.java异常类介绍. Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类. 有一篇比较好的blog,http://blog.csdn.net/hguisu/article/details/6155636 介绍java异常. 二.异常处理方式. 在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常. 三.程序中使用. 2种处理方式 1.throw new 异常  在方法体上写throws

第65课 C++中的异常处理(下)

1. C++中的异常处理 (1)catch语句块可以抛出异常 ①catch中获捕的异常可以被重新抛出 ②抛出的异常需要外层的try-catch块来捕获 ③catch(…)块中抛异常的方法是throw;也是将所有异常重新抛出 (2)catch块中重新抛异常的意义 ①可以被外层try-catch块捕获,并重新解释异常的信息. ②工程开发中使用这样的方式统一异常类型 A.假设我们的私有库使用到了第3方的库函数,如func. B.但其抛出的异常类型为int*类型,很不友好.我们可以在私有库使用func的

C++、Java、JavaScript中的异常处理(Exception)

编程思想之异常处理 什么叫异常处理? 什么叫异常(Exception)?顾名思义就是非正常的情况,出现了不希望出现的意外,异常处理就是遇到这种意外时准备的对策和解决方案.比如您开着一辆劳斯莱斯在公路上行走,突然前面出现一个小孩,幸好您眼疾手快紧急刹车,而避免了一场交通事故.在这个例子中突然出现的小孩就是异常,紧急刹车就是异常处理(面对这种突发情况采取的解决方案). 程序来源于现实,是现实的抽象和模拟,也会有异常,因此异常的处理就显示的极为重要.试想如果您的手机的某个应用使用两下就崩溃了,或都出现

Struts2中的异常处理

因为在Action的execute方法声明时就抛出了Exception异常,所以我们无需再execute方法中捕捉异常,仅需在struts.xml 中配置异常处理. 为了使用Struts2的异常处理机制,必须打开Struts2的异常映射功能,这需要exception拦截器.在struts-default.xml文件中已经开启了exception拦截器. 声明式异常捕捉 Struts2的异常处理机制是通过在struts.xml文件中配置<exception-mapping--/>元素完成的,配置

编写高质量代码改善C#程序的157个建议——建议85:Task中的异常处理

建议85:Task中的异常处理 在任何时候,异常处理都是非常重要的一个环节.多线程与并行编程中尤其是这样.如果不处理这些后台任务中的异常,应用程序将会莫名其妙的退出.处理那些不是主线程(如果是窗体程序,那就是UI主线程)产生的异常,最终的办法都是将其包装到主线程上. 在任务并行库中,如果对任务运行Wait.WaitAny.WaitAll等方法,或者求Result属性,都能捕获到AggregateException异常.可以将AggregateException异常看做是任务并行库编程中最上层的异

【转】Java中关于异常处理的十个最佳实践

原文地址:http://www.searchsoa.com.cn/showcontent_71960.htm 导读:异常处理是书写强健Java应用的一个重要部分,Java许你创建新的异常,并通过使用 throw 和 throws关键字抛出它们. 异常处理是书写强健Java应用的一个重要部分,它是关乎每个应用的一个非功能性需求,是为了优雅的处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java提供了几个异常处理特性,以try,catch和 finally 关键字的形式内建于语言自身之中

Java中的异常处理机制

Java中的异常处理机制 示例 public class test { public static void main(String[] args) { // TODO Auto-generated method stub String s="hello"; int i=Integer.parseInt(s); } } 运行异常结果 在上述代码中Integer.parseInt表示把字符串类型转化成整数类型,同时Integer是Int的封装类:程序会报错,因为计算机没有办法把hello

[查异常网]-20160331-谈谈J2EE项目中的异常处理

为什么要在J2EE项目中谈异常处理呢?可能许多java初学者都想说:“异常处理不就是try….catch…finally吗?这谁都会啊!”.笔者在初学java时也是这样认为的.如何在一个多层的j2ee项目中定义相应的异常类?在项目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checked Exception转化成unchecked Exception ,何时需要把unChecked Exception转化成checked Exception?异常是否应该

Java 编程中关于异常处理的 10 个最佳实践

异常处理是书写 强健 Java应用的一个重要部分.它是关乎每个应用的一个非功能性需求,是为了优雅的处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java提供了几个异常处理特性,以try,catch和finally 关键字的形式内建于语言自身之中.Java编程语言也允许你创建新的异常,并通过使用  throw 和 throws关键字抛出它们.事实上,异常处理不仅仅是知道语法.书写一个强健的代码更多的是一门艺术而不仅仅是一门科学,这里我们将讨论一些关于异常处理的Java最佳实践.这些 J

struts2学习笔记(6)------配置struts2中的异常处理

我们平常的异常处理是直接在方法中手动捕捉异常,当捕捉到特定异常后,返回特定逻辑视图名.这样的缺点是代码与异常处理耦合太多,一旦要改变异常处理方式,需要修改代码! struts2提供给了一种更好的方式来处理异常------声明式的方式管理异常处理,我们可以通过再方法里将出现的异常throw出去,抛给struts2框架处理,然后再struts2中默认开启着异常映射功能,该功能在struts-default.xml中配置的一个拦截器,如下: <interceptor name="exceptio