C++学习随笔13 面向对象编程(6) - 异常

  • 异常使用

1.框定异常(try语句块):在祖先函数处,框定可能产生错误的语句序列。它是异常的根据。若不框定异常,则没有异常这回事。

2.定义异常处理语句(catch语句):将出现异常后的处理过程放在catch块中,以便当异常被抛出,因类型匹配而捕捉时,就处理之。

3.抛掷异常(throw语句):在可能产生异常的语句中进行错误检测,如果有错,则抛出异常。

前两个步骤是在同一个函数体中定义的,而抛掷异常则可以跨函数使用。

  • 捕捉异常
  1. 类型匹配
 1 class A{};
 2 class B{};
 3 
 4 int main(){
 5      try{
 6          int j = 0;
 7          double d = 2.3;
 8          char str[20] = "Hello";
 9          cout<<"Please input a exception number:";
10          int a; cin >> a;
11          switch(a){
12               case 1: throw d;             //不允许使用类型名
13               case 2: throw j;
14               case 3: throw str;
15               case 4: throw A();
16               case 5: throw B();
17               default: cout<<"No throws here.\n"; 
18          }catch(int){                      //catch代码块必须出现在try块之后,并且在try块之后可以出现多个catch代码块,以捕捉各种不同类型的抛掷
19                cout<<"int exception.\n";
20          }catch(double){                   //捕捉处理并不一定需要实参传递
21                cout<<"double exception.\n";
22          }catch(char *){                   //类型必须严格匹配不存在隐式转换,如果抛出一个char类型的实体,则没有捕捉类型被匹配
23                cout<<"char * exception.\n";
24          }catch(A){
25                cout<<"class A exception.\n";  throw;   //由于抛掷的是同样的异常,所以抛掷类型可以省略
26          }catch(B){
27                cout<<"class B exception.\n";
28          }catch(...){ cout<<"Any kind of error.\n";}   //捕捉任何类型的异常
29          cout <<"That's OK.\n";
30 }

异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作。因此发生异常现象的地方,一定是某个实体出了差错,该实体及其所对应的数据类型便作为抛掷和捕捉的依据。

  • 异常的申述

异常抛掷后总是沿着函数调用链往上,直到被某个函数捕捉住。异常沿着函数调用链向上抛掷的特点,给上游函数造成了一定的压力,因为要做好捕捉的准备工作。由于模块的独立性,使得调用模块者有可能不清楚下游模块中抛掷出来的异常类型,因而即使采用彻底捕捉的手段捕捉住这种异常,也不会处理这种异常。

上下游函数的联系,可以通过异常申述来获得。异常申述就是在函数声明函数定义的头部加上可能抛掷的异常集合。例如:

1 void f() throw(A, B);  //f函数内可能会抛掷出A和B类型的异常
2 void g();              //g函数内可能会抛掷出任何类型的异常
3 void h() throw();      //h函数内不会抛掷出任何异常,如果h函数真的抛掷出异常,或者f函数抛掷出非A类非B类异常,称为未料到异常(unexpect exception)
  • 异常类
 1 class MyException{     //自定义异常基类
 2 public:
 3     virtual const char* what(){ return "MyException";}
 4 };
 5 class HardwareErr : public MyException{
 6 public:
 7     virtual const char* what(){ return "HardwareErr ";}
 8 };
 9 class PerformErr: public MyException{
10 public:
11     virtual const char* what(){ return "PerformErr";}
12 };
13 
14 int main(){
15      //......
16      try{
17           for(int i=0; i<10; ++i) metalGroupProcess(i);
18      }catch(MyException &me){   //为了表现异常处理的多态,异常捕捉的类型采用了异常的引用
19             if(string(me.what()) == "HardwareErr") 
20                        //......
21             if(string(me.what()) == "PerformErr") 
22                        //......
23      }catch(exception &e){     //使用标准库类库出现的异常的异常基类
24             cout<<"StandardException: "<<typeid(e).name()<<"\n";
25      }
26 }
  • 异常的应用
  1. 构造函数的错误处理

构造函数因为没有返回类型,无法通过返回值来报告运行状态,所以只能通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。

      2. 引用的动态转型

动态转型专门针对多态编程,动态转型一个基类指针到子类指针,通过测试指针值是否为0,可以确定所指向的对象是子类对象还是其他对象。但是通过动态转型一个基类引用到子类引用,不能通过测试引用值来断定引用的类型。引用动态转型失败时,C++运行系统会自发地抛出一个标准异常exception的子类bad_cast异常

      3. typeid的用法

typeid能够多态地抓取对象的类型信息。获取对象的类型信息,便可以进行所需的多态处理。typeid在typeinfo头文件中定义,typeid(objectName)为对象的类型,类型可以比较。typeid(objectName).name为用C串表示的该类型的名称,可以用来打印类型信息。

typeid括号中的参数可以为对象、引用和指针,因为希望对象表现多态,所以typeid的参数多是指针和引用。如果参数是指针,那么0值将引起bad_typeid标准异常。bad_typeid也是标准异常的一个子类。它与bad_cast异常一起,都在typeinfo头文件中定义。

posted @ 2016-12-14 15:34  etcjd  阅读(182)  评论(0)    收藏  举报