C++ 异常处理(基础篇)

来源:互联网 发布:网红拍照软件 编辑:程序博客网 时间:2024/05/22 01:58

异常处理:

不同的编译器处理异常时的表现不同,有的崩溃,有的其它形式

希望编写所有编译器上都以相同的受控方式运行的代码

 

方式1:调用abort()、exit()

(在程序出现异常时,将退出程序)

方式2:返回错误代码

例如:程序正常运行时返回true,异常时返回false,或设置一个全局变量,在程序异常时,设置为一个特殊的值

(在程序出现异常时,不退出程序,能继续执行)

方式3:异常机制

(在程序出现异常时,能给出匹配的异常类型,并继续执行)

 

1>异常处理的组成部分

throw “... ”:引发异常,其后的值指出了异常的特征

catch ():捕获异常,括号中指出了异常处理程序要响应的异常类型

try { } :指出需要注意大括号的中代码,引发的异常,可能引发异常的函数,必须放在try块内,否则将无法处理异常

2>异常处理的执行次序

throw语句终止函数执行,导致程序沿函数调用序列后退,直到找到包含try块的函数,

找到后,匹配try块后面的catch块类型,匹配成功后,执行catch块中的语句

3>缺失情况

如果引发了异常,而没有try块或没有匹配的处理程序时,在默认情况下,程序将调用abort()函数【这种行为可以修改】

4>catch()块中的异常类型

异常类型可以是字符串、其他C++类型,通常为类类型

例子:bad_hmean异常类

class bad_hmean

{

    private:

     double v1;

     double v2;

    public:

     bad_hmean(int a=0,int b=0):v1(a),v2(b){};

     void mesg();

}

inline void bad_hmean::mesg()

{

    std::cout<<"hmean("<<v1<<", "<<v2<<"):"<<"invalid arguments: a=-b\n‘;

}

5>异常规范

异常规范由throw和异常类型列表组成,加在函数定义的后面。对于引发多种异常的函数,可提供一个由逗号分隔的异常类型列表组成

例如:double multi(double a,double b)  throw(const char *s,double);

函数定义应包含原型中的信息:

double hmean(double a,double b) throw(bad_hmean)

{

   if(a==-b) 

   throw bad_hmean(a,b);

   else

   return 2.0*a*b/(a+b);

}

如果异常规范中的括号内为空,则表明该函数不会引发异常

double se(double x) throw();

6>堆栈解退

假设try块没有直接调用引发异常的函数,而是调用了对引发异常的函数进行调用的函数,则程序流程将从引发异常的函数跳到包含try块的处理程序的函数


函数正常返回:函数通常都返回到调用它的函数,一次类推,同时每个函数都在结束时释放其自动变量,自动变量是类对象则调用类的析构函数

函数由于出现异常(不是由于返回)而终止:则程序将会释放堆栈中的内存,但不会在释放堆栈的第一个返回地址后停止,而是继续释放堆栈,直到找到一个位于try块中的返回地址。随后,控制权将转到块尾的异常处理程序,而不是函数调用后面的第一条语句。这个过程就是堆栈解退。

函数返回仅仅处理该函数放在堆栈中的对象,而throw语句则处理try块和throw之间整个函数调用序列放在堆栈中的对象

7>异常规范补充。。。

例子:

try{

   z=means(x,y);

}

catch(bad_hmean &bh){}

catch(bad_gmean &bg){}


double means(double a,double b) throw(bad_hmean,bad_gmean) //异常规范不仅要包含函数本身引发的异常,

{ //还应包含该函数调用的其它函数引发的异常,依次类推,means()调用了gmean(),因此它应宣称自己能传递gmean()引发的异常

   try{

    hm=hmean(a,b);

    gm=gmean(a,b);

}

catch(bad_hmean &bh){...throw}  //执行完会再跳到main()函数中的try块

}

8>throw-catch与return的区别

区别1:函数function()的return语句将控制权返回到调用function()的函数

throw-catch语句则将控制权向上返回到第一个包含能够捕获相应异常的try-catch组合

区别2:引发异常时,编译器将创建一个临时拷贝,即使异常规范和catch块中指定的是引用

例子:

try{

  super();

}

catch(problem &p){}  //p指向oop的拷贝而不是oop本身,super()执行完毕后,oop将消失


void super() throw(problem)

{problem oop;    

throw oop;}     

问题:既然throw将生成拷贝,为什么代码中使用引用呢?

答:基类的引用可以指向派生类对象,基类引用与任何派生类对象匹配


9>catch块排列顺序

引发的异常对象将被第一个与之匹配的catch块捕获,所以catch块的排列顺序应该与派生类的顺序相反

例子:

class bad1{};

class bad2: public bad1{};

class bad3: public bad2{};


void duper() throw(bad1)

{

  if()  throw bad1();

  if()  throw bad2();

  if()  throw bad3();

}


try{

  duper();

}

catch(bad3 &b3) {}

catch(bad2 &b2) {}

catch(bad1  &b1)  {}  //基类与所有它的派生类匹配

10>补充

假设编写了一个调用另一个函数的函数,但不知道被调用的函数可能引发哪些异常,在这种情况下,使用省略号表示异常类型

catch(.....){}

原创粉丝点击