异常基本概念ios
异常即程序在运行期间产生的非预期性错误,如内存配额不足、文件不存在、网络不可用、SQL语句非法等。express
异常分为可恢复性异常和不可恢复性异常。网络
异常是否可恢复,不一样的应用场景将有不一样的理解。例如,对于内存配合不足,在短暂的峰涌状况下内存可以快速恢复则属于可恢复性异常,由于一但被释放和回收,程序将回到常态;若内存长时间得不到释放,则属于不可恢复性异常,此时应当终止程序的运行。函数
异常又可分为已知异常和未知异常。已知异常是指程序编写者事先考虑到的异常,对异常有清晰的定义和处理逻辑,并在程序中进行捕捉;未知异常是指程序编写者事先未察觉到的异常,能够理解为程序bug。this
异常捕获是一把双刃剑,对异常进行显示捕获,尤为对于bug型的未知异常,须要找出产生异常的缘由和代码进行修复,即须要考虑异常的分析定位机制。所以,对于未知异常或者没法处理的异常,不该当使用catch(…)笼统的进行捕获,当不须要关心异常或者没法处理异常的时候,就应该避免捕获异常或者将未处理的异常再次抛出,以便交由bugreport统一对未处理异常进行捕获、分析。spa
函数的异常声明指针
在函数的声明后面使用throw关键字可对函数的异常产生进行限制。code
不容许函数抛出任何异常对象
void f() throw(); 表示f函数不容许抛出任何异常;以VS2005编译器为例,若在f函数中抛出异常,编译时会有以下提示:内存
warning C4297: 'f' : function assumed not to throw an exception but does
1> __declspec(nothrow) or throw() was specified on the function
但异常仍是能正常抛出和被捕获。
容许函数抛出任意异常
void f() throw(…);表示容许f函数抛出任意异常。
容许函数抛出指定类型的异常
void f() throw(MyException1,MyException2);表示容许函数f抛出MyException1和MyException2两种类型的异常。以vs2005为例,在f中抛出其余类型的异常,编译未见提示,且异常能正常抛出和被捕获。
异常的抛出
使用关键字throw抛出异常,有以下两种语法。
throw express;抛出express所表明的表达式的异常,异常类型为表达式的值的类型,异常的值为表达式的值。例如,
try{ throw 10; }catch(int e){ //progress.... }
throw;将异常继续“向上传递”。例如,
try{ throw 10; }catch(int e){ throw... }
异常类型
任何类型均可以做为异常类型,包括基本数据类型,如int、double、char等;还包括类类型,如std:string;还包括自定义异常类型,即从std::exception派生的子类。
异常对象的传递
自定义的异常类:CCustomException
#pragma once #include <string> using namespace std; class CCustomException : public std::exception { public: CCustomException(void); CCustomException(const char*); ~CCustomException(void); std::string getMsg(); void setMsg(std::string msg); virtual const char* what() const; private: std::string m_msg; };
#include "StdAfx.h" #include "CustomException.h" CCustomException::CCustomException(void) { } CCustomException::~CCustomException(void) { } CCustomException::CCustomException(const char* msg) { m_msg = msg; } std::string CCustomException::getMsg() { return m_msg; } void CCustomException::setMsg(std::string msg) { m_msg = msg; } const char* CCustomException::what() const { return m_msg.c_str(); }
main函数以下:
// ExceptionExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "CustomException.h" #include <iostream> void exceptionTest1() throw(CCustomException) { throw CCustomException("this is CCustomException Test1!"); return; } void exceptionTest2() throw(CCustomException*) { throw new CCustomException("this is CCustomException Test2!"); return; } void exceptionTest3() throw(CCustomException&) { throw(CCustomException("this is CCustomException Test3!")); return; } void Test1() { try { exceptionTest1(); } catch (std::exception e) { cout<<e.what()<<endl; } } void Test2() { try { exceptionTest2(); } catch (std::exception *e) { cout<<e->what()<<endl; delete e; } } void Test3() { try { exceptionTest3(); } catch (std::exception &e) { cout<<e.what()<<endl; } } int _tmain(int argc, _TCHAR* argv[]) { Test1(); Test2(); Test3(); getchar(); return 0; }
输出结果以下:
异常对象的传递和函数参数的传递同样,有值传递、指针传递和引用传递3种传递方式。
值传递会产生临时对象拷贝,且不支持多态。,虽然CCustomException是std::exception的子类,可是仍然不能调用CCustomException的what函数,输出了Unknown Message。
指针传递
指针传递能够实现多态,但会将临时对象的地址做为指针传出去,出现悬挂指针错误。若是在堆上分配内存空间,又不知道什么时候恰当的删除对象,二来代码可读性、可维护性遭到破坏,违反了内存空间从哪里申请获得就在哪里释放的内存分配释放原则。
值引用传递既能避免临时对象的拷贝,又能支持多态,且没有对象释放问题。推荐采用值引用的方式传递异常对象。输出结果为:This is CCustomException Test3.
catch的匹配规则
异常匹配采用自底向上的匹配顺序进行匹配,即先在异常产生的函数中进行匹配,若该形成该异常的代码没有被try…catch…捕获或者catch类型不匹配,则向上传递直到匹配到第一条catch结束匹配,或则未发现任何匹配的catch则产生未处理异常交由运行时环境处理。
对于基本数据类型的异常,采用严格类型匹配机制,不支持类型转换
对于自定义类型,基类类型可以匹配子类异常对象,子类类型不能匹配基本异常对象。
C++提供了强大的异常处理机制将异常产生和异常处理的代码分离,即将程序的正常业务逻辑和错误处理逻辑进行分离,并在不改变函数原型的状况下,实现异常对象的“向上”传递。
C++的异常处理机制有3部分组成:try(检查),throw(抛出),catch(捕获)。把须要检查的语句放在try模块中,检查语句发生错误,throw抛出异常,发出错误信息,由catch来捕获异常信息,并加以处理。通常throw抛出的异常要和catch所捕获的异常类型所匹配。异常处理的通常格式为:
try { 被检查语句 throw 异常 } catch(异常类型1) { 进行异常处理的语句1 } catch(异常类型2) { 进行异常处理的语句2 }