C++ 异常机制

来源:互联网 发布:2013网络歌手 编辑:程序博客网 时间:2024/05/16 15:23

一,C++异常机制的作用

C++异常是对程序在运行过程中发生异常情况的一种响应,异常提供了将程序的控制权从程序的一部分传递到另一部分的响应。异常的抛出和处理主要使用了以下三个关键字: try throw  catch 

(1)try:放在try块中的代码是可能发生异常的代码。

(2)throw:这个关键字引发异常,紧随其后的值指出了异常的特征。throw语句实际上是跳转,即命令程序跳到另一语句。throw语句必须包含在try块中,也可以是被包含在外层函数的try块中。

(3)catch:捕获异常,随后是位于括号中的类型声明,它指出了异常处理程序要响应的异常类型。

try-catch语句形式如下:

try{
        包含可能抛出异常的语句;
}
catch(类型名 [形参名]){  // 捕获特定类型的异常
        //异常处理程序
}
catch(...) {  // 三个点则表示捕获所有类型的异常

        //异常处理程序

}


二,使用异常机制处理异常

double fun(double x, double y){    if(y == 0){        throw y;  //除数为0,抛出异常    }    return x / y;}int main(){    double res;    try{        res = fun(2, 0);    } catch(double val){        cout<<"除数为:"<<val<<endl;        exit(1);    }    return 0;}
 

三,使用自定义的对象作为异常类型

使用自定义的对象作为异常类型的优点:

1,可以使用不同的异常类型来区分引发不同的异常。

2,对象可以携带信息,程序员可以根据这些信息来确定引发异常的信息。

class bad_func{private:    int v1;public:    bad_func(int v1){        this->v1 = v1;    }    void mesg(){        cout<<"除数不能为:"<<v1<<endl;    }};double fun(double x, double y){    if(y == 0){        throw bad_func(0);  //除数为0,抛出异常    }    return x / y;}int main(){    double res;    try{        res = fun(2, 0);    } catch(bad_func &exp){        exp.mesg();        exit(1);    }    return 0;}


四,栈解退

假设在try块没有直接调用引发异常的函数,而是调用了另外一个会引发异常的函数,则程序的流程将从引发异常的函数直接跳到包含try块和处理程序的函数,这涉及到栈解退。

栈解退的过程如下:

如果一个函数由于异常而终止,程序将进行出栈的操作,但是不会在回退到栈的第一个返回地址后停止,而是继续回退栈,直到找到一个位于try块中的返回地址。然后控制权交给块尾的处理程序,而不是函数调用后的第一条语句,这个过程称为栈解退。

void funcA();void funcB();void funcC();void funcA(){    funcB();}void funcB(){    funcC();}void funcC(){    throw 0;  //抛出一个异常}int main(){    double res;    try{        funcA();    } catch(int val){        exit(1);    }    return 0;}

funcC()函数引发了一个异常,此时会回退到调用funcC()的funcB(),在funcB()中发现funcC()并没有放到try块中,即在funcB()中没有对异常进行处理;此时会回退到调用funcB()的funcA(),同样在funcA()中发现funcB()并没有放到try块中,此时会回退到调用funcA()的main()函数,在main()函数中funcA()是放在try块中,此时程序的控制权就跳转到main()函数中。


五,throw-catch语句与函数参数与函数返回机制的区别

(1),函数func()中的返回语句将控制权返回到调用func()的函数,但是throw语句将控制权向上返回到第一个这样的函数,即包含能够捕获异常的try-catch块。

(2),引发异常时编译器总是创建异常对象的一个临时拷贝,即catch块中的参数是引用类型。

class bad_func{private:    int v1;public:    bad_func(int v1){        this->v1 = v1;        cout<<"bad_func构造函数"<<endl;    }    void mesg(){        cout<<"除数不能为:"<<v1<<endl;    }    bad_func(const bad_func &p){        this->v1 = p.v1;        cout<<"bad_func复制构造函数"<<endl;    }    ~bad_func(){        cout<<"bad_func析构函数: "<<this<<endl;    }};double fun(double x, double y){    if(y == 0){        bad_func obj(0);        cout<<"bad_func对象地址: "<<&obj<<endl;        throw obj;  //除数为0,抛出异常    }    return x / y;}int main(){    double res;    try{        fun(2, 0);    } catch(bad_func &p){        cout<<"bad_func对象地址: "<<&p<<endl;        exit(1);    }    return 0;}

输出结果

bad_func构造函数bad_func对象地址: 0x69fe6cbad_func复制构造函数bad_func析构函数: 0x69fe6cbad_func对象地址: 0x6f0dc0bad_func析构函数: 0x6f0dc0Process returned 1 (0x1)   execution time : 0.008 sPress any key to continue.

六,exception类

较新的C++编译器将异常合并到语音中。例如,为了支持该语言,exception头文件定义了exception类作为基类。其中有一个名为what()的虚函数,它返回一个字符串,该字符串的特征随实现而异。

class bad_func : exception{private:    int v1;public:    bad_func(int v1){        this->v1 = v1;        cout<<"bad_func构造函数"<<endl;    }    bad_func(const bad_func &p){        this->v1 = p.v1;        cout<<"bad_func复制构造函数"<<endl;    }    //重写虚函数what()    const char* what(){        cout<<"除数不能为0.";    }};double fun(double x, double y){    if(y == 0){        bad_func obj(0);        cout<<"bad_func对象地址: "<<&obj<<endl;        throw obj;  //除数为0,抛出异常    }    return x / y;}int main(){    double res;    try{        fun(2, 0);    } catch(bad_func &p){        cout<<"bad_func对象地址: "<<&p<<endl;        p.what();        exit(1);    }    return 0;}

七,bad_alloc异常与new

使用new导致内存分配问题,C++的最新的处理方式是让new引发bad_alloc异常,头文件new包含bad_alloc的声明,它是从exception类公有派生而来的。但是在以前,当无法分配请求的内存量时,new返回一个空指针。

struct Big{    double stuff[200000];};int main(){    Big *p;    try{        p = new Big[10000];    } catch(bad_alloc &p){        cout<<p.what()<<endl;        exit(1);    }    cout<<"success."<<endl;    return 0;}


八,C++在函数声明后面添加throw关键字

C++函数声明后面添加throw关键字,是对这个函数的异常安全性做出限制。

void fun() throw();  //表示fun不允许抛出任何形式的异常,fun是异常安全的。void fun() throw(...);  //表示fun可以抛出任何形式的异常。void fun() throw(Type);  //表示fun可以抛出Type类型异常。
(1),函数后面声明 throw() 只是接口的提供者和接口的使用者间的默契或称协议。

(2),声明一个不抛出异常的函数后,你有责任保证在你的函数的实现里面不会抛出异常。

(3),对于一个抛出指定抛出异常类型的函数,实际抛出的异常要与指定的异常一致。

void getName() throw(double){    int num = 5;    throw(num);}int main(){    try{        getName();    } catch(int){        cout<<"catch error."<<endl;    }}
程序分析:

上面声明了一个抛出double类型异常的函数,但是实际抛出的是int类型的异常。不论自己在catch块中捕获的是int类型还是double类型的异常,程序在运行期间都会终止运行。

0 0