29.C++- 异常处理

C++内置了异常处理的语法元素 try catch

try语句处理正常代码逻辑

  • try语句发现异常时,则通过throw语句抛出异常,并退出try语句

catch语句处理异常情况

  • throw语句抛出异常时,则会直接跳到catch语句处理
  • catch语句允许被重载,在try语句后面可以有多个catch语句
  • 不同类型的异常由不同的catch语句捕获,顺序从上往下严格匹配,不会进行隐式转换,比如:
throw 1;       //由int型的catch语句捕获
throw 1.5;     //由double型的catch语句捕获
throw 1.5f;    //由float型的catch语句捕获
throw 'A';      //由char型的catch语句捕获
throw "ABC";    //由char const *型的catch语句捕获
throw string("ABC");      //由string型的catch语句捕获
  • cath(...)语句,表示捕获前面所有没被定义的异常,且只能放在所有catch语句的末尾,比如:
    try
    {
              throw 1.55;          //直接退出try语句,跳转到满足条件的catch语句
              throw "ERR";   
    }
    catch(int i)                //只捕获int型异常
    {
              cout<<i<<endl;
    }
    catch(string s)           //只捕获string型异常
    {
        cout<<s<<endl;
    }
    catch(...)                //捕获前面所有没被定义的异常
    {
        cout<<"cath(...)"<<endl;
    }

运行打印:

cath(...)

 

 

throw抛出的异常必须被catch处理

如果throw抛出的异常,在当前函数没有catch语句能捕获,则会返回到上一级函数里再寻找catch语句,直到被处理为止,否则程序将结束运行,如下图:

 

 

 

在catch语句块中可以将捕获的异常重新抛出

catch抛出的异常,则需要通过外层的try...catch...捕获

如果是catch(...)语句,则直接填throw;即可,编译器会自动将捕获的异常重新抛出

比如:

void internal()
{
             try
            {
                     throw 1;
            }
             catch(...)
            {
                     cout<< "internal: catch(...)"<<endl;
                     throw;
            }
}

int main()
{

   try
   {
             internal();      
   }

   catch(int i)
   {
          switch(i)
          {
             case 1 :                                            //1对应超时
                     cout<<"timeout"<<endl;  break;
              case 2 :                                           //2对应实参有误
                     cout<<"invalid argument"<<endl; break;
              case 3 :                                           //3对应运行异常
                     cout<<"runtime exception"<<endl; break;
          } 
   }
   return 0;

}

运行打印:

internal: catch(...)
timeout

 

catch中重新抛出异常的意义

举个例子,当我们调用第三方库的func()函数,但是该func()函数返回的异常是int型,每个异常值意义大有不同 (每次查看异常值都需要翻看文档手册才行)

所以我们可以在自己库创建一个myfunc()函数,通过try...catch...再次封装func()函数,将异常值重新解释为其它类型(比如const char *),然后再次抛出.

以后调用myfunc()函数,获取的异常信息就是const char *类型了.

 

如果catch中抛出的类型是类的情况

  • 需要将捕获子类异常的catch放在上部
  • 捕获父类异常的cath放在下部, 避免子类异常当做父类异常来使用.

比如:

#include <iostream>
#include <string>
using namespace std; class Base { };
class Exception : public Base { int m_id; //异常值 string m_desc; //异常值描述信息 public: Exception(int id, string desc) { m_id = id; m_desc = desc; }
int id() const { return m_id; }
string description() const { return m_desc; } }; /* 假设: 当前的函数式第三方库中的函数,因此,我们无法修改源代码 函数名: void func(int i) 抛出异常的类型: int -1 ==》 参数异常 -2 ==》 运行异常 -3 ==》 超时异常 */ void func(int i) { if( i < 0 ) { throw -1; } if( i > 100 ) { throw -2; } if( i == 11 ) { throw -3; } cout << "Run func..." << endl; } void MyFunc(int i) { try { func(i); } catch(int i) { switch(i) { case -1: throw Exception(-1, "Invalid Parameter"); //生成一个子类对象,并抛出 break; case -2: throw Exception(-2, "Runtime Exception"); break; case -3: throw Exception(-3, "Timeout Exception"); break; } } } int main(int argc, char *argv[]) { try { MyFunc(11); } catch(const Exception& e) //捕获子类异常的catch放在上部 { cout << "Exception Info: " << endl; cout << " ID: " << e.id() << endl; cout << " Description: " << e.description() << endl; } catch(const Base& e) //捕获父类异常的catch放在下部 { cout << "catch(const Base& e)" << endl; } return 0; }

运行打印:

Exception Info:
   ID: -3
   Description: Timeout Exception

 

在C++标准库中提供了异常类

头文件 : <stdexcept>

标准库中的异常都是从exception类派生的

exception类主要有两个分支

- logic_error

用于程序中可避免的逻辑错误,在程序运行之前,就能被检测到

logic_error类派生了以下几种类:

  • domain_error(const string& )    :   专业领域内的范畴
  • invalid_argument(const string& )   :  无效参数,比如对unsigned型进行负数操作
  • length_error(const string& )  :    长度异常,比如字符串附加太多字符
  • out_of_range(const string&)     :    超出范围,比如数组下标越界
  • 它们都有一个what()成员函数,用来返回一个字符串异常信息

 

-runtime_error

常用于程序中无法避免的恶性错误,只在程序运行时才能被检测到

logic_error类派生了以下几种类:

  • range_error(const string& )  :内部计算时发生区间错误
  • overflow_error(const string& )  :算数运算时发生上溢
  • underflow_error(const string& )  :算数运算时发生下溢
  • 它们都有一个what()成员函数,用来返回一个字符串异常信息

比如:

#include <iostream> 
#include <stdexcept>
using namespace std; template <typename T, int N > class Array { T ma[N];public: Array() { for(int i=0;i<N;i++) ma[N]=0; } T& operator [] (int index) { if((index>=0)&&(index<N)) { return ma[index]; } else //数组下标越界 { throw out_of_range("T& operator [] (int index)"); //抛出一个 out_of_range类 } } }; int main() { try { Array<int,5> arr; arr[10]=100; } catch(out_of_range& exc) { cout<< exc.what()<<endl; //打印捕获到out_of_range类的异常信息 cout<< " Line: " << __LINE__ <<", Function: "<< __FUNCTION__ << endl; //打印当前行 } return 0; }

运行打印:

T& operator [] (int index)
Line: 41, Function: main

 

 

posted @ 2018-04-14 16:14  诺谦  阅读(928)  评论(0编辑  收藏  举报