c++——异常

来源:互联网 发布:mac家装设计软件 编辑:程序博客网 时间:2024/06/05 10:07

人们在使用不同软件系统时,可能会遇到各种各样的异常或错误,软件运行中产生的异常给应用带来不便,因此在软件设计中,在保证软件能正确完成任务的同时,还应该使之具有良好的容错性。

传统的异常处理方法          通常采用系统提供的中断函数或指令     void abort()    中断程序执行,返回主c++窗口      stdlib.hvoid assert()   若表达式值为false,则中断程序执行并显示文件和行号 assert.hvoid exit()  中断程序执行,返回退出代码        stdlib.hreturn 表达式     终止函数执行返回表达式的值

传统异常处理方法优点是异常处理直接,系统运行开销小,适用于处理简单的局部错误与异常,不足之处是异常代码分布于程序中可能出错的各个地方,异常处理代码与系统的功能代码互相混杂在一起,降低了程序的可读性和可维护性,不适用于大型软件的开发。

c++ 异常处理机制

基本思想:将异常检测与异常处理分离即产生异常的函数不一定需要异常处理能力,当一个函数发生异常时,它抛出所发生的异常或错误,由调用者捕获或处理异常若上层调用函数仍不具有处理异常能力,还可以进一步抛出异常,按调用层次关系传递给更上一层处理一直可以传给c++运行系统,由系统终止程序运行

c++异常处理机制是一种不唤醒机制,抛出异常的函数或模块一旦将异常抛出,将不再恢复程序的执行,系统将有序的释放资源。

异常,让一个函数可以在发现自己无法处理的错误时抛出一个异常,希望它的调用者可以直接或者间接处理这个问题。而传统错误处理技术,检查到一个局部无法处理的问题时:

1.终止程序(例如atol,atoi,输入NULL,会产生段错误,导致程序异常退出,如果没有core文件,找问题的人一定会发疯)

2.返回一个表示错误的值(很多系统函数都是这样,例如malloc,内存不足,分配失败,返回NULL指针)

3.返回一个合法值,让程序处于某种非法的状态(最坑爹的东西,有些第三方库真会这样)

4.调用一个预先准备好在出现”错误”的情况下用的函数。

第一种情况是不允许的,无条件终止程序的库无法运用到不能当机的程序里。第二种情况,比较常用,但是有时不合适,例如返回错误码是int,每个调用都要检查错误值,极不方便,也容易让程序规模加倍(但是要精确控制逻辑,我觉得这种方式不错)。第三种情况,很容易误导调用者,万一调用者没有去检查全局变量errno或者通过其他方式检查错误,那是一个灾难,而且这种方式在并发的情况下不能很好工作。至于第四种情况,本人觉得比较少用,而且回调的代码不该多出现。

使用异常,就把错误和处理分开来,由库函数抛出异常,由调用者捕获这个异常,调用者就可以知道程序函数库调用出现错误了,并去处理,而是否终止程序就把握在调用者手里了。

异常的传播方式和调用链相反,这称之为栈展开。在异常发生的地方,编译器必须完成以下的事情:
如果throw发生在try区块中,寻找匹配的cath语句,如果找不到,则当前函数立刻返回,并往上一级调用函数中寻找匹配的catch语句,该动作将一直重复直到有合适的catch语句出现。如果一直到最后都没有发现合适的catch语句,系统将调用terminate,而默认情况下,terminate将调用abort结束整个进程。
这里写图片描述

但是,错误的处理依然是一件很困难的事情,C++的异常机制为程序员提供了一种处理错误的方式,使程序员可以更自然的方式处理错误。

//异常处理语句try{   语句;}catch(类型一,参数一)       //捕获异常{  与类型一相关的异常处理语句;}catch(类型二,参数二){  与类型二相关的异常处理语句;}catch(类型三,参数三){  与类型三相关的异常处理语句;}......throw  表达式;   //抛出异常

用一个简单代码块来理解

#include <iostream>using namespace std;double div(double x, double y){    if (x == 0)        throw y;   //抛出异常    return y / x;}int main(){    double x, y;    try    {        cout << "PLEASE input y =";        cin >> y;        cout << "PLEASE input x =";        cin >> x;        cout << "q =" << div(x, y) << endl;    }    catch (double)   //捕获异常    {        cout << "divided by zero,error" << endl;    }    return 0;}
当用户输入 被除数x = 0时,函数div()将发生异常,由throw抛出,再又catch捕获异常,执行异常处理语句,并且不再返回div()函数继续执行。

异常处理中的构造函数与析构

#include <iostream>using namespace std;void Testfun();class A{public:    A()    {        cout << "构造函数    A" << endl;    }    ~A()    {        cout << "析构函数    A" << endl;    }    void Dispiay()    {        cout << "class A   " << endl;    }};class B{public:    B();    ~B();};B::B(){    cout << "构造函数  B" << endl;}B::~B(){    cout << "析构函数  B" << endl;}void Testfun(){    B b;    cout << "throw A " << endl;    throw A();    //抛出异常 }int main(){    try    {        cout << "new call Testfun()" << endl;        Testfun();    }    catch (A a)     //捕获处理类异常    {        cout << "A" << endl;        a.Dispiay();    }    catch ( ...)    //可以捕获任意类型的异常    {        cout << "other" << endl;    }    return 0;}
异常捕获的匹配原则:异常对象的类型与catch说明符的类型必须完全匹配,只有以下情况例外1、允许从非constconst 类型转换2、允许从派生类类型到基类类型的转换3、将数组转换为数组类型的指针,将函数转换为指向函数类型的指针
1、构造函数完成对象的构造与初始化,需要保证不要再构造函数中抛出异常,否则可能会对对象的不完全初始化2、析构函数中也不要抛出异常,防止内存资源泄露或句柄未关闭

异常的优点:
1、异常允许上层应用如何处理在底层嵌套函数中发生的“不可能发生”的失败,不像出错代码的记录那么模糊费解
2、许多c++第三方库使用异常,关闭异常将导致难于与之结合
3、在测试框架等新兴代码中,异常更便于代码维护

0 0
原创粉丝点击