C++学习随笔13 面向对象编程(6) - 异常
- 异常使用
1.框定异常(try语句块):在祖先函数处,框定可能产生错误的语句序列。它是异常的根据。若不框定异常,则没有异常这回事。
2.定义异常处理语句(catch语句):将出现异常后的处理过程放在catch块中,以便当异常被抛出,因类型匹配而捕捉时,就处理之。
3.抛掷异常(throw语句):在可能产生异常的语句中进行错误检测,如果有错,则抛出异常。
前两个步骤是在同一个函数体中定义的,而抛掷异常则可以跨函数使用。
- 捕捉异常
- 类型匹配
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 }
- 异常的应用
- 构造函数的错误处理
构造函数因为没有返回类型,无法通过返回值来报告运行状态,所以只能通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。
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头文件中定义。

浙公网安备 33010602011771号