异常,当一个函数发现一个无法处理的错误时抛出异常,让函数的
调用者直接或间接的处理这个问题。
异常处理,是一种允许两个独立开发的程序组件在程序执行期间遇到程序不正常的
情况(称为异常,exception )时相互通信的机制。
异常抛出:
throw (表达式)
异常发现与抛出异常:
/*发现异常并且抛出异常*/try{ //可能出现异常的语句}
捕获异常:
catch(类型名 + 形参名) //捕获特定类型异常{ }
catch(...) //捕获任意类型异常(在不确定异常类型时使用) {}
下来看一个简单的例子:
#includeusing namespace std;int Div(int a, int b){ return a/b;}int main(){ Div(1,0); system("pause"); return 0;}
结果显示:
程序直接崩溃,由于调用时传入第二个参数是零,零不能作除数,所以会崩溃,下面使用异常处理这个问题:
#include#include using namespace std;int Div(int a, int b){ if(b == 0) { throw string("parameter error.");//异常抛出 } return a/b;}int main(){ try { Div(1, 0);//发现异常,与抛出异常 } catch(const string& S) { cout< <
程序可以正常运行,也可以看到异常出现的地方,这样就可以很清楚的解决这个问题。
下面再看个了例子:
#include#include using namespace std;void test(){ int* p = new int(1); if(1) { throw string("error."); } delete p; }int main(){ try { test(); } catch(const string& S) { cout< <
结果显示:
这个程序运行貌似没有问题,其实问题大了。
void test(){ int* p = new int(1); if(1) { throw string("error."); //程序执行到这里,直接就去catch()那块了, //导致new出来的内存没有释放,造成内存泄漏。 } delete p; }
内存泄漏:
会导致你开辟出来的那块内存以后就不可以用了,这样多泄漏几次,你的电脑就哈哈了,会很卡,很卡。
内存泄漏危害:
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。
当然,这里并不是说内存泄漏,下面可以用异常来处理上面这个问题:
#include#include using namespace std;void test(){ int* p = new int(1); try//发现异常 { if(1) { throw string("error.");//异常抛出 } } catch(...)//捕获异常 { delete p;//释放开辟的空间 throw;//异常重新抛出 } delete p; }int main(){ try//发现重新抛出的异常 { test(); } catch(const string& S)//捕获异常 { cout< <
结果显示:
这样就不会出现内存泄漏,也会很清楚的在控制台显示错误的信息。
异常重新抛出:在异常处理过程中也可能存在单个catch 子句不能完全处理异常的情况。在某些修
正动作之后,catch 子句可能决定该异常必须由函数调用链中更上级的函数来处理那么catch子句可以通过重新抛出(rethrow )该异常把异常传递给函数调用链中更上级的另一个catch子句,rethrow 表达式的形式为:
throw;
rethrow 表达式重新抛出该异常对象rethrow 只能出现在catch 子句的复合语句中。被重新抛出的对象就是其原来的异常对象。
栈展开:
在查找用来处理被抛出异常的catch 子句时因为异常而退出复合语句和函数定义这个过程
抛出异常的时候,将暂停当前函数的执行,开始查找对应的匹配catch语句。首先检查throw本身是否在catch块内部,如果是,再查找匹配的catch语句。如果有匹配的,则处理。没有则退出当前函数栈,继续在调用函数的栈中进行查找。不断重复上述过程。若到达main函数的栈,依旧没有匹配的,则终止程序。
上述这个沿着调用链查找匹配的catch语句的过程称为栈展开。找到匹配的catch语句并处理以后,会继续沿着catch语句后继继续执行。
异常捕获的匹配规则:
异常对象的类型与catch说明符的类型必须完全匹配。
只有以下几种情况例外:
1. 允许从非const对象到const的转换。
2. 允许从派生类型到基类类型的转换。
3. 将数组转换为指向数组类型的指针,将函数转换为指向函数类型的指针。
异常处理总结:
异常处理就是通过throw,try,catch,这三个关键字,来发现异常,并抛出异常,捕获异常,可以做出相应的处理。(这只是简单的异常处理)