欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

29.C++- 异常处理

程序员文章站 2022-10-25 09:06:12
C++内置了异常处理的语法元素 try catch try语句处理正常代码逻辑 当try语句发现异常时,则通过throw语句抛出异常,并退出try语句 catch语句处理异常情况 当throw语句抛出异常时,则会直接跳到catch语句处理 catch语句允许被重载,在try语句后面可以有多个catc ......

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语句,直到被处理为止,否则程序将结束运行,如下图:

 29.C++- 异常处理

 

 

在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