C++异常处理

程序中的错误分为编译时的错误和运行时的错误。编译时的错误主要是语法错误,比如:句尾没有加分号,括号不匹配,关键字错误等,这类错误比较容易修改,因为编译系统会指出错误在第几行,什么错误。而运行时的错误则不容易修改,因为其中的错误是不可预料的,或者可以预料但无法避免的,比如内存空间不够,或者在调用函数时,出现数组越界等错误。如果对于这些错误没有采取有效的防范措施,那么往往会得不到正确的运行结果,程序不正常终止或严重的会出现死机现象。我们把程序运行时的错误统称为异常,对异常处理称为异常处理。C++中所提供的异常处理机制结构清晰,在一定程度上可以保证程序的健壮性。

  C++中处理异常的过程是这样的:在执行程序发生异常,可以不在本函数中处理,而是抛出一个错误信息,把它传递给上一级的函数来解决,上一级解决不了,再传给其上一级,由其上一级处理。如此逐级上传,直到最高一级还无法处理的话,运行系统会自动调用系统函数terminate,由它调用abort终止程序。这样的异常处理方法使得异常引发和处理机制分离,而不在同一个函数中处理。这使得底层函数只需要解决实际的任务,而不必过多考虑对异常的处理,而把异常处理的任务交给上一层函数去处理。


1、C++异常处理语法 

  C++的异常处理机制有3部分组成:try(检查),throw(抛出),catch(捕获)。把需要检查的语句放在try模块中,检查语句发生错误,throw抛出异常,发出错误信息,由catch来捕获异常信息,并加以处理。一般throw抛出的异常要和catch所捕获的异常类型所匹配。异常处理的一般格式为:

  try

  {
    被检查语句
    throw 异常
  }
  catch(异常类型1)
  {
    进行异常处理的语句1
  }
  catch(异常类型2)
  {
    进行异常处理的语句2
  }
  ...

下面我们用示例演示一下异常处理:

 #include "stdafx.h"
  #include <iostream>
  
  template <typename T>
  T Div(T x,T y)
  {
  if(y==0)
  throw y;//抛出异常
  return x/y;
 }
 
 int main()
 {
 int x=5,y=0;
 double x1=5.5,y1=0.0;
 try
     {
 //被检查的语句
         std::cout<<x<<"/"<<y<<"="<<Div(x,y)<<std::endl;
         std::cout<<x1<<"/"<<y1<<"="<<Div(x1,y1)<<std::endl;
     }
 catch(int)//异常类型
     {
         std::cout<<"除数为0,计算错误!"<<std::endl;//异常处理语句
     }
 catch(double)//异常类型
     {
         std::cout<<"除数为0.0,计算错误!"<<std::endl;//异常处理语句
     }
 
 return0;
 }


结果:

看了上述的示例代码,也许有人会问,第二个双精度类型的除法计算也应该抛出异常才对啊,在实际的运行过程中并非如此,其实该双精度类型除法函数根本没有被执行过。以上程序的执行规程为:调用函数Div(x,y)时发生异常,由函数Div中的语句"throw y"抛出异常,并不在往下执行return x/y,接着catch捕获int类型的异常并处理异常,最后直接执行"return 0"。因此函数Div(x1,y1)和catch(double){}模块根本没有被执行。如果,我们把y的值改为1,则结果就变成为:

如果在执行try语句模块时,没有发生异常,则catch语句块不起作用,流程转到其后的语句继续执行。从上述两个结果中可知第一次throw抛出的int类型所以找到处理该类型的catch,而第二次是抛出double类型所找到的是处理double类型的catch。

  下面对异常处理补充几点:(1)try和catch块中必须要用花括号括起来,即使花括号内只有一个语句也不能省略花括号;(2)try和catch必须成对出现,一个try_catch结果中只能有一个try块,但可以有多个catch块,以便与不同的异常信息匹配;(3)如果在catch块中没有指定异常信息的类型,而用删节号"...",则表示它可以捕获任何类型的异常信息;(4)如果throw不包括任何表达式,表示它把当前正在处理的异常信息再次抛出,传给其上一层的catch来处理;(5)C++中一旦抛出一个异常,如果程序没有任何的捕获,那么系统将会自动调用一个系统函数terminate,由它调用abort终止程序;

例子:

  #include "stdafx.h"
  #include <iostream>
  
  template <typename T>
  T Div(T x,T y)
  {
  if(y==0)
  throw y;//抛出异常
  return x/y;
 }
 
 int main()
 {
int x=5,y=1;
 double x1=5.5,y1=0.0;
 try
     {
 //被检查的语句
         std::cout<<x<<"/"<<y<<"="<<Div(x,y)<<std::endl;
         std::cout<<x1<<"/"<<y1<<"="<<Div(x1,y1)<<std::endl;
     }
 catch(...)//捕获任意类型异常
     {
 try
         {
             std::cout<<"任意类型异常!"<<std::endl;
 throw;//抛出当前处理异常信息给上一层catch
         }
 catch(int)//异常类型
         {
             std::cout<<"除数为0,计算错误!"<<std::endl;//异常处理语句
         }
catch(double)//异常类型
         {
            std::cout<<"除数为0.0,计算错误!"<<std::endl;//异常处理语句
        }
     
     }
    
 return0;
 }



结果:

在C++中,throw抛出异常的特点有: 
   (1)可以抛出基本数据类型异常,如int和char等; 
   (2)可以抛出复杂数据类型异常,如结构体(在C++中结构体也是类)和类; 
   (3)C++的异常处理必须由调用者主动检查。一旦抛出异常,而程序不捕获的话,那么abort()函数就会被调用,弹出如图1所示的对话框,程序被终止; 
   (4)可以在函数头后加throw([type-ID-list])给出异常规格,声明其能抛出什么类型的异常。type-ID-list是一个可选项,其中包括了一个或多个类型的名字,它们之间以逗号分隔。如果函数没有异常规格指定,则可以抛出任意类型的异常。

2 标准异常

namespace std 

  //exception派生 
  class logic_error; //逻辑错误,在程序运行前可以检测出来 
  //logic_error派生 
  class domain_error; //违反了前置条件 
  class invalid_argument; //指出函数的一个无效参数 
  class length_error; //指出有一个超过类型size_t的最大可表现值长度的对象的企图 
  class out_of_range; //参数越界 
  class bad_cast; //在运行时类型识别中有一个无效的dynamic_cast表达式 
  class bad_typeid; //报告在表达试typeid(*p)中有一个空指针p 
  //exception派生 
  class runtime_error; //运行时错误,仅在程序运行中检测到 
  //runtime_error派生 
  class range_error; //违反后置条件 
  class overflow_error; //报告一个算术溢出 
  class bad_alloc; //存储分配错误 
}  
   请注意观察上述类的层次结构,可以看出,标准异常都派生自一个公共的基类exception。基类包含必要的多态性函数提供异常描述,可以被重载。下面是exception类的原型: 
class exception 

  public: 
   exception() throw(); 
   exception(const exception& rhs) throw(); 
   exception& operator=(const exception& rhs) throw(); 
   virtual ~exception() throw(); 
   virtual const char *wh
}

3、异常处理函数

在标准C++中,还定义了数个异常处理的相关函数和类型(包含在头文件<exception>中): 
namespace std 

  //EH类型 
  class bad_exception; 
  class exception; 
  typedef void (*terminate_handler)(); 
  typedef void (*unexpected_handler)(); 
  // 函数 
  terminate_handler set_terminate(terminate_handler) throw(); 
  unexpected_handler set_unexpected(unexpected_handler) throw(); 
  void terminate(); 
  void unexpected(); 
  bool uncaught_exception(); 
}  
   其中的terminate相关函数与未被捕获的异常有关,如果一种异常没有被指定catch模块,则将导致terminate()函数被调用,terminate()函数中会调用ahort()函数来终止程序。可以通过set_terminate(terminate_handler)函数为terminate()专门指定要调用的函数,例如: 
#include <cstdio> 
#include <exception> 
using namespace std; 
//定义Point结构体(类) 
typedef struct tagPoint 

  int x; 
  int y; 
} Point; 
//扔出Point异常的函数 
static void f() 

  Point p; 
  p.x = 0; 
  p.y = 0; 
  throw p; 

//set_terminate将指定的函数 
void terminateFunc() 

  printf("set_terminate指定的函数\n"); 

int main() 

  set_terminate(terminateFunc); 
  try 
  { 
   f(); //抛出Point异常 
  } 
  catch (int) //捕获int异常 
  { 
   printf("捕获到int异常"); 
  } 
  //Point将不能被捕获到,引发terminateFunc函数被执行 
  return 0; 
}  
   这个程序将在控制台上输出 "set_terminate指定的函数" 字符串,因为Point类型的异常没有被捕获到。当然,它也会弹出图1所示对话框(因为调用了abort()函数)。 
   上述给出的仅仅是一个set_terminate指定函数的例子。在实际工程中,往往使用set_terminate指定的函数进行一些清除性的工作,其后再调用exit(int)函数终止程序。这样,abort()函数就不会被调用了,也不会输出图1所示对话框。 
   关于标准C++的异常处理,还包含一些比较复杂的技巧和内容,我们可以查阅《more effective C++》的条款9~条款15。

参考:

http://www.cnblogs.com/CaiNiaoZJ/archive/2011/08/19/2145349.html

http://blog.chinaunix.net/uid-21411227-id-1826957.html

3 c++ primer page 580~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值