c++异常
目录
异常抛出和捕获的流程
异常抛出类型的匹配方式
异常抛出和捕获的流程
1.程序出现问题时,我们通过抛出(throw)一个对象来引发一个异常,该对象的类型和当前函数的调用链决定了应该有那个catch来捕获处理这个异常。
2.该catch处理程序是该调用链中匹配对象类型且离抛出异常位置最近的那一个。
3.当throw执行后,throw后面的语句将不再执行。程序的执行将从对应匹配的catch开始,catch可能是同一函数中的局部catch,也可能是调用链中的另一个函数中的catch,控制权将从throw位置转移到catch位置。(注意这里会发生两个事情:1.沿着调用链的函数可能会因此提早退出不执行throw后面的程序了。2.一旦程序开始指向异常处理程序,沿着调用链创建的对象都将被销毁。)
4.异常抛出后会创建一个异常拷贝对象,因为异常抛出的对象可能是一个局部的对象,所以会生成一个拷贝对象,这个拷贝对象会在catch中销毁。(类似于函数的传值返回)
异常抛出类型的匹配方式
1.一般来说抛出的对象的类型与catch是类型完全匹配的。如果有多个类型匹配的那么就找最近的那个。
2.但是也有一些例外,允许从非常量类型向常量类型的转换,也就是权限缩小;允许数值转化成指向数组元素的指针;函数被转换成指向函数的指针。允许从派生类到基类的转换,这点很实用,实际中的继承体系基本都是用这个方式设计的。(实践中我们一般抛出的异常的类型是派生类,捕获的类型是基类)
3.如果最后到main函数都没有找到匹配的catch就会终止程序,但是一般来说我们并不想终止程序,所以main函数中最后都有一个catch(...),她可以捕获任意类型的异常,但是我们就无法知道程序因为什么而出现了异常。
异常的重新抛出
有时catch捕获到一个对象之后需要对这个错误进行分类,对于某些特殊的错误需要特殊处理。其他的错误则重新抛出异常给外层调用链捕获。直接throw;就可以把捕获的对象重新抛出。
#include<iostream>
#include<map>
#include<cmath>
#include<queue>
#include<string>
#include<math.h>
#include<assert.h>
#include<stack>
#include<vector>
#include<set>
#include<unordered_map>
using namespace std;
class exception
{
public:exception(const string& str,int id):errid(id),errmsg(str){}virtual string what() const{return errmsg;}int getid() const{return errid;}
private:string errmsg;int errid;
};
double divide(const int& a, const int& b)
{try{if (b == 0){string s = "divode by zero";throw s;//抛异常}else return (double)a / (double)b;}catch (int errid){cout << errid << endl;}return 0;
}
void func()
{int a, b;cin >> a >> b;try{cout << divide(a, b) << endl;}catch(const char* errmsg){cout << errmsg << endl;}cout << __FUNCTION__ << ":" << __LINE__ << "行执行" << endl;
}
int main()
{while (1){try{func();}catch (const string& errmsg){cout << errmsg << endl;}catch(...)//没有被捕获的异常会跳到这里{cout << "未知异常" << endl;}}return 0;
}