C++异常捕捉与处理的深入讲解 在阅读别人开发的项目中也许你会经常看到了多处使用异常的代码也许你也很少遇见使用异常处理的代码。那在什么时候该使用异常又在什么时候不该使用异常呢在学习完异常基本概念和语法之后后面会有讲解。1异常抛出和捕捉语句123456789//1.抛出异常throw异常对象//2.异常捕捉try{可能会发生异常的代码}catch(异常对象){异常处理代码}throw子句throw 子句用于抛出异常被抛出的异常可以是C的内置类型例如 throw int(1);也可以是自定义类型。try区段这个区段中包含了可能发生异常的代码在发生了异常之后需要通过throw抛出。catch子句每个catch子句都代表着一种异常的处理。catch子句用于处理特定类型的异常。catch块的参数推荐采用地址传递而不是值传递不仅可以提高效率还可以利用对象的多态性。2异常的处理规则throw抛出的异常类型与catch抓取的异常类型要一致throw抛出的异常类型可以是子类对象catch可以是父类对象catch块的参数推荐采用地址传递而不是值传递不仅可以提高效率还可以利用对象的多态性。另外派生类的异常捕获要放到父类异常扑获的前面否则派生类的异常无法被扑获如果使用catch参数中使用基类捕获派生类对象一定要使用传递引用的方式例如catch (exception e)异常是通过抛出对象而引发的该对象的类型决定了应该激活哪个处理代码被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个在try的语句块内声明的变量在外部是不可以访问的即使是在catch子句内也不可以访问栈展开会沿着嵌套函数的调用链不断查找直到找到了已抛出的异常匹配的catch子句。如果抛出的异常一直没有函数捕获(catch)则会一直上传到c运行系统那里导致整个程序的终止。3实例实例1抛出自定义类型异常。123456789101112131415161718192021222324252627282930313233343536373839404142classData{public:Data() {}};voidfun(intn){if(n0)throw0;//抛异常 int异常if(n1)throwerror;//抛字符串异常if(n2){Data data;throwdata;}if(n3){throw1.0;}}intmain(){try{fun(6);//当异常发生fun里面fun以下代码就不会再执行调到catch处执行异常处理代码后继续执行catch以外的代码。当throw抛出异常后没有catch捕捉则整个程序会退出不会执行整个程序的以下代码cout*************endl;}catch(inti) {coutiendl;}catch(constchar*ptr){coutptrendl;}catch(Data d){coutdataendl;}catch(...)//抓取 前面异常以外的所有其他异常{coutallendl;}return0;}实例2标准出错类抛出和捕捉异常。12345678910111213#include iostreamusingnamespacestd;intmain(){try{char* p newchar[0x7fffffff];//抛出异常}catch(exception e){cout e.what() endl;//捕获异常然后程序结束}return0;}输出结果当使用new进行开空间时申请内存失败系统就会抛出异常不用用户自定义异常类型此时捕获到异常时就可告诉使用者是哪里的错误便于修改。实例3继承标准出错类的派生类的异常抛出和捕捉。1234567891011121314151617181920212223242526272829303132333435363738394041#include iostream#include exception#include sys/types.h#include sys/stat.h#include fcntl.husingnamespacestd;classFileException :publicexception{public:FileException(string msg) {this-exStr msg;}virtualconstchar*what()constnoexcept//声明这个函数不能再抛异常{returnthis-exStr.c_str();}protected:string exStr;};voidfun(){intfd ::open(./open.txt,O_RDWR);if(fd0){FileException openFail(open fail);//创建异常对象throwopenFail;//抛异常}}intmain( ){try{fun();}catch(exception e) {//一般需要使用引用coute.what()endl;}coutendendl;return0;}当文件不存在时输出结果如果在Linux上运行上述代码需要根据环境修改98标准写法1234567~FileException()throw(){}//必须要virtualconstchar*what()constthrow()//声明这个函数不能再抛异常{returnthis-exStr.c_str();}//编译g main.cpp2011标准写法1234567~FileException()noexcept{}//必须要virtualconstchar*what()constnoexcept//声明这个函数不能再抛异常{returnthis-exStr.c_str();}//编译g main.cpp -stdc11 指定用c11标准编译4总结1. 使用异常处理的优点传统错误处理技术检查到一个错误只会返回退出码或者终止程序等等我们只知道有错误但不能更清楚知道是哪种错误。使用异常把错误和处理分开来由库函数抛出异常由调用者捕获这个异常调用者就可以知道程序函数库调用出现的错误是什么错误并去处理而是否终止程序就把握在调用者手里了。2. 使用异常的缺点如果使用异常光凭查看代码是很难评估程序的控制流函数返回点可能在你意料之外这就导致了代码管理和调试的困难。启动异常使得生成的二进制文件体积变大延长了编译时间还可能会增加地址空间的压力。C没有垃圾回收机制资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。 这个需要使用RAII来处理资源的管理问题。学习成本较高。C标准库的异常体系定义得不好导致大家各自定义各自的异常体系非常的混乱。3. 什么时候使用异常建议除非已有的项目或底层库中使用了异常要不然尽量不要使用异常虽然提供了方便但是开销也大。4. 程序所有的异常都可以catch到吗并非如此只有发生异常并且又抛出异常的情况才能被catch到。例如数组下标访问越界的情况系统是不会自身抛出异常的所以我们无论怎么catch都是无效的在这种情况我们需要自定义抛出类型判断数组下标是否越界然后再根据自身需要throw自定义异常对象这样才可以catch到异常并进行进一步处理。