C++中的异常处理

1、C++异常处理

创新互联建站是一家专注网站建设、网络营销策划、重庆小程序开发、电子商务建设、网络推广、移动互联开发、研究、服务为一体的技术型公司。公司成立10年以来,已经为成百上千家成都酒店设计各业的企业公司提供互联网服务。现在,服务的成百上千家客户与我们一路同行,见证我们的成长;未来,我们一起分享成功的喜悦。

(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 "RunException";

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

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

新闻名称:C++中的异常处理
本文路径:https://www.cdcxhl.com/article6/ipgeig.html

成都网站建设公司_创新互联,为您提供网站设计公司虚拟主机面包屑导航网站制作品牌网站建设建站公司

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

h5响应式网站建设