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