第65课 C++中的异常处理(下)
1. C++中的异常处理
(1)catch语句块可以抛出异常
①catch中获捕的异常可以被重新抛出
②抛出的异常需要外层的try-catch块来捕获
③catch(…)块中抛异常的方法是throw;也是将所有异常重新抛出
(2)catch块中重新抛异常的意义
①可以被外层try-catch块捕获,并重新解释异常的信息。
②工程开发中使用这样的方式统一异常类型
A.假设我们的私有库使用到了第3方的库函数,如func。
B.但其抛出的异常类型为int*类型,很不友好。我们可以在私有库使用func的地方捕获这个异常,并在catch块中重新解释这个异常并抛出为我们自定义的Exception类型.
【编程实验】异常的重新解释
#include <iostream> using namespace std; //演示在catch块中可以抛出异常 void Demo() { try { try { throw 'c'; } catch(int i) { cout << "Inner:catch(int i)" << endl; throw i; //重新抛出异常 } catch(...) { cout << "Inner:catch(...)" << endl; throw; //抛出所有类型的异常 } }catch(...) { cout << "Outer:catch(...)" << endl; } } /* 假设:当前的函数是第三方库中的函数。因此,我们无法修改源代码 函数名: void func(int i) 抛出异常的类型:int -1 ==> 参数异常 -2 ==> 运行异常 -3 ==> 超时异常 */ void func(int i) { ) { ; } ) { ; } ) { ; } //正常运时时 cout << "Call func(int i)" << endl; } //以下是我们的私有库,当中使用到了第3方的库,这里需要将第3方库的 //异常类型统一成我们的异常信息格式 void MyFunc( int i) { try { func(i); } catch(int i) { switch(i) { : throw "Invalid Parameter"; break; : throw "Runtime Exception"; break; : throw "Timeout Exception"; break; } } } int main() { Demo(); cout << endl; try { MyFunc(); } catch(const char* cs) { cout << "Exception info: " << cs << endl; } ; }; /*输出结果: Inner:catch(...) Outer:catch(...) Exception info: Timeout Exception */
2. 自定义异常类类型
(1)对于类类型异常的匹配依旧是至上而下严格匹配
(2)赋值兼容性原则在异常匹配中依然适用,因此一般而言:
①匹配子类异常的catch放在上部
②匹配父类异常的catch放在下部(否则如果放上面,则子类异常由于赋值兼容会被父类捕获)。
(3)工程中会定义一系列的异常类,每个类代表可能出现的一种异常类型。
(4)代码复用时可能需要重解释不同的异常类(如由于继承的层次关系,很可能父类的异常类在子类中会被重新解释)
(5)在定义catch语句块时推荐使用引用作为参数
【编程实验】类类型的异常
#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 //考虑const Exception对象时,函数加const { return m_id; } string description() const { return m_desc; } }; /* 假设:当前的函数是第三方库中的函数。因此,我们无法修改源代码 函数名: void func(int i) 抛出异常的类型:int -1 ==> 参数异常 -2 ==> 运行异常 -3 ==> 超时异常 */ void func(int i) { ) { ; } ) { ; } ) { ; } //正常运时时 cout << "Call func(int i)" << endl; } //以下是我们的私有库,当中使用到了第3方的库,这里需要将第3方库的 //异常类型统一成我们的异常信息格式 void MyFunc( int i) { try { func(i); } catch(int i) { switch(i) { : , "Invalid Parameter"); break; : , "Runtime Exception"); break; : , "Timeout Exception"); break; } } } int main() { try { MyFunc(); } catch(const Exception& e) //注意使用引用以防止拷贝 { cout << "Exception info: " << endl; cout << " ID: " << e.id() << endl; cout << " Description: " << e.description() << endl; } catch(const Base& e) //父类异常放下部 { cout << "catch(const Base& e)" << endl; } ; }; /*输出结果: Exception info: ID: -3 Description: Timeout Exception */
3.C++标准库中提供的异常类族
(1)标准库(#include <stdexcept>)中的异常都是从exception类派生的
(2)exception类有两个主要的分支
①logic_error:常用于程序中的可避免的逻辑错误
②runtime_error:常用于程序中无法避免的恶性错误
(3)标准库中的异常
【编程实验】标准库中的异常使用
//Array.h
#ifndef _ARRAY_H_ #define _ARRAY_H_ #include <stdexcept> //引入异常类 using namespace std; template <typename T, int N> class Array { T m_array[N]; public: int length() const; bool set(int index, T value); bool get(int index, T& value); T& operator[](int index); T operator[](int index) const; virtual ~Array(); }; template < typename T, int N> int Array<T, N>::length() const { return N; } template < typename T, int N> bool Array<T, N>::set(int index, T value) { <= index) && (index < N); if (ret) { m_array[index] = value; } return ret; } template < typename T, int N> bool Array<T, N>::get(int index, T& value) { <= index) && (index < N); if (ret) { value = m_array[index]; } return ret; } template < typename T, int N> T& Array<T, N>::operator[](int index) { <= index) && (index < N)) { return m_array[index]; } else { //out_of_range是标准库中的异常类 throw out_of_range("T& Array<T, N>::operator[](int index)"); } } template < typename T, int N> T Array<T, N>::operator[](int index) const { <= index) && (index < N)) { return m_array[index]; } else { //out_of_range是标准库中的异常类 throw out_of_range("T Array<T, N>::operator[](int index) const"); } } template < typename T, int N> Array<T, N>::~Array() { } #endif
//HeapArray.h
#ifndef _HEAPARRAY_H_ #define _HEAPARRAY_H_ #include <stdexcept> //引入异常类 using namespace std; template < typename T> class HeapArray { private: int m_length; T* m_pointer; HeapArray(int len); HeapArray(const HeapArray<T>& obj); bool construct(); public: static HeapArray<T>* NewInstance(int length); static void ReleaseInstance(const HeapArray<T>* pointer); int length() const; bool get(int index, T& value); bool set(int index, T value); T& operator[](int index); T operator[](int index) const; //与上面函数构成重载关系 HeapArray<T>& self() const; HeapArray<T>& self(); //与上面函数构成重载关系 ~HeapArray(); }; template <typename T> HeapArray<T>::HeapArray(int len) { m_length = len; } template <typename T> HeapArray<T>::HeapArray(const HeapArray<T>& obj) { m_pointer = obj.m_pointer; m_length = obj.m_length; } template <typename T> bool HeapArray<T>::construct() { m_pointer = new T[m_length]; return m_pointer != NULL; } template <typename T> HeapArray<T>* HeapArray<T>::NewInstance(int len) { HeapArray<T>* ret = new HeapArray<T>(len); if (!(ret && ret->construct())) { delete ret; ret = ; } return ret; } template <typename T> void HeapArray<T>::ReleaseInstance(const HeapArray<T>* pointer) { delete pointer; } template <typename T> int HeapArray<T>::length() const { return m_length; } template <typename T> bool HeapArray<T>::get(int index, T& value) { <= index)&&(index < m_length)); if(ret) { value = m_pointer[index]; } return ret; } template <typename T> bool HeapArray<T>::set(int index, T value) { <= index)&&(index < m_length)); if(ret) { m_pointer[index] = value ; } return ret; } template <typename T> T& HeapArray<T>::operator[](int index) { <= index)&&(index < m_length)) { return m_pointer[index]; } else { throw out_of_range("T& HeapArray<T>::operator[](int index)"); } } template <typename T> T HeapArray<T>::operator[](int index) const { <= index)&&(index < m_length)) { return m_pointer[index]; } else { throw out_of_range("T HeapArray<T>::operator[](int index) const"); } } template <typename T> HeapArray<T>& HeapArray<T>::self() const { return *this; } template <typename T> HeapArray<T>& HeapArray<T>::self() { return *this; } template <typename T> HeapArray<T>::~HeapArray() { delete[] m_pointer; } #endif
//test.cpp
#include <iostream> #include <string> #include <memory> //for auto_ptr #include "Array.h" #include "HeapArray.h" using namespace std; void TestArray() { Array<> a; ; i<a.length(); i++) { a[i] = i; } ; i<; i++) { cout << a[i] << endl; } } void TestHeapArray() { //使用智能指针,目的是自动释放堆空间 auto_ptr< HeapArray<)); if(pa.get() != NULL) { HeapArray<double>& array = pa->self(); ; i<array.length(); i++) { array[i] = i; } ; i<; i++) { cout << array[i] << endl; } } } int main() { //试验1:观察异常出错的信息 TestArray(); cout << endl; TestHeapArray(); //试验2: 观察异常出错的信息 // try // { // TestArray(); // cout << endl; // TestHeapArray(); // } // catch(...) // { // cout << "Exception" << endl; // } ; };
4. C++异常机制中没提供finally块解决方案
(1)RAII技术(Resource Aquisition Is Initialization,资源获得即初始化)
①基本的思路:通过一个局部对象来表现资源,于是局部对象的析构函数将会释放资源。即,将资源封装成一个类,将资源的初始化封装在构造函数里,释放封装在析构函数里。要使用资源的时候,就实例化一个局部对象。
②在抛出异常的时候,由于局部对象脱离了作用域,自动调用析构函数,会保证资源被释放。
(2)具体做法
①直接使用局部变量。
try{ File f("xxx.ttt"); //使用局部对象 //其他操作 } //异常发生时和正常情况下,文件资源都在这里被释放 catch { //... }
②将资源封装在一个类中
class Test{ public: File *file;//资源被封装起来 Test() { file = new file(...); } ~Test() { delete file;} }; try{ Test t; //使用局部对象 //其他操作 } //无论什么情况下,t在这里被释放,同时调用析构函数 catch { //... }
③使用智能指针
try { std::auto_ptr<file> pfile = new file(); //.... } //无论什么情况,智能指针都在这里被释放 catch(...) { //.... }
5. 构造函数中抛出异常的问题
①问题:构造未完成,析构函数将不被调用,可能造成内存泄漏。因此,一般不建议在构造函数中抛出异常
【编程实验】构造函数中抛异常
#include <iostream> using namespace std; class Test { private: int* p; public: Test() { cout<< "Test()" << endl; p= ]; ; //抛出异常,构造函数非正常结束 } ~Test() { cout <<"~Test()" << endl; delete[] p; } }; int main() { try { Test t; //t未完成构造就出异常,就跳到catch语句去, //注意t的作用域为try的{},因没被执行完,所以 //还没调用析构函数就跳到catch作用域中—内存泄漏 } catch (int e) { cout <<"Catch:"<<e << endl; } ; } /*输出结果 Test() Catch:10 */
②解决方案:
【方案1】在构造函数中捕获异常、做善后处理后重新抛出异常
#include <iostream> using namespace std; class Test { private: int* p1; int* p2; public: Test() { ; cout<< "Test()" << endl; try { nStep = ; p1 = ]; if( p1 == NULL) { //delete[] p1; throw nStep; //制造异常 } nStep = ; p2 = ]; if( p2 == NULL ) { //delete p2; throw nStep; //制造异常 } } catch(int n) { //构造函数中发生异常时,析构函数不会被调用,所以 //要自己做善后处理! ) delete[] p1; //第1步出错时,销毁p1堆空间 ) delete[] p2;//第2步出错时,销毁p2\p1堆空间 throw n; //继续抛出,通知调用者构造函数调用失败 } } ~Test() { cout <<"~Test()" << endl; delete[] p1; delete[] p2; } }; int main() { try { Test t; //Test的构造函数中可能发生异常 } catch (int e) { cout <<"Catch:"<<e << endl; } ; }
【方案2】 二阶构造法(可参考《第27课 二阶构造模式》)
6. 小结
(1)catch语句块中可以抛出异常
(2)异常的类型可以是自定义类类型
(3)赋值兼容性原则在异常匹配中依然适用
(4)标准库中的异常都是从exception类派生的。
第65课 C++中的异常处理(下)的更多相关文章
- 第64课 C++中的异常处理(上)
1. C++内置的异常处理:try-catch (1)try语句处理正常代码逻辑 (2)catch语句处理异常情况 (3)try语句中的异常由对应的catch语句处理,如果对应的catch中没有处理该 ...
- C++中的异常处理(下)
array.h #ifndef _ARRAY_H_ #define _ARRAY_H_ #include <stdexcept> using namespace std; template ...
- 第18课 - make 中的路径搜索(下)
第18课 - make 中的路径搜索(下) 1. 问题一 当 VPATH 和 vpath 同时出现,make 会如何处理? 工程项目的目录结构如下图所示,src1 和 src2 中都包含了 func. ...
- C++中的异常处理(一)
来自:CSDN 卡尔 后续有C++中的异常处理(二)和C++中的异常处理(三),C++中的异常处理(二)是对动态分配内存后内部发生错误情况的处理方法,C++中的异常处理(三)中是使用时的异常说明. ...
- java 中的异常处理
一. 异常的概念和Java异常体系结构 异常是程序运行过程中出现的错误.本文主要讲授的是Java语言的异常处理.Java语言的异常处理框架, 是Java语言健壮性的一个重要体现. Java把 ...
- Delphi中的异常处理(10种异常来源、处理、精确处理)
一.异常的来源 在Delphi应用程序中,下列的情况都比较有可能产生异常. 1.文件处理 2.内存分配 3.windows资源 4.运行时创建对象和窗体 5.硬件和操作系统冲突 6.网络问题 7.数据 ...
- 【ASP.NET Web API教程】4.3 ASP.NET Web API中的异常处理
原文:[ASP.NET Web API教程]4.3 ASP.NET Web API中的异常处理 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内 ...
- 【转】Java中关于异常处理的十个最佳实践
原文地址:http://www.searchsoa.com.cn/showcontent_71960.htm 导读:异常处理是书写强健Java应用的一个重要部分,Java许你创建新的异常,并通过使用 ...
- T-SQL编程中的异常处理-异常捕获(catch)与抛出异常(throw)
本文出处: http://www.cnblogs.com/wy123/p/6743515.html T-SQL编程与应用程序一样,都有异常处理机制,比如异常的捕获与异常的抛出,本文简单介绍异常捕获与异 ...
随机推荐
- app名字后面的描述怎么加?
构建一个最新的版本 更改一下 名称即可
- 我的android学习经历5
android在strings.xml文件中,写string对象时,如何加入空格 <string name="password">密 码:</string& ...
- 在IDEA 上使用Git查看主干和分支等
使用IDEA 查看提交历史时,IDEA默认显示的页面是你当前页面的提交历史: 然后,你点击左侧的 log 键,则可以显示整个团队每个人的提交记录: 在选中每一次的记录以后,会弹出每次提交的所有文件(对 ...
- 试用 Nexus OSS 3.0 的docker仓库 (一)
Nexus 3.0 可以创建三种docker仓库: 1. docker (proxy) 代理和缓存远程仓库 ,只能pull 2. docker (hosted) 托管仓库 ,私有仓库, ...
- 在VC环境下执行代码出现错误
这是在执行代码过程中出现的错误,源代码在别的电脑上能运行,在自己的VC里运行就出现错误,在网上也搜过解决办法,但还是有点不太理解,是编程环境的问题h还是代码本身也存在问题???
- SQL Server(一)——数据库基础知识
SQL:Structured Quety Language SQL SERVER是一个以客户/服务器(c/s)模式访问.使用Transact-SQL语言的关系型数据库管理子系统(RDBMS) DBMS ...
- SSRS 的简单使用(二)
经过上一篇的初始,我们已经做好了报表的准备工作,接下来我们进行报表的展示和其他一下操作,并且给出一些使用RS的方法方便大家日后能灵活使用. 步骤: 1.首先拖拽表格等进入到设计模板 点击 ...
- column 'id' in field list is ambiguous
column 'id' in field list is ambiguous 这个错误,是因为你查询语句里面有id字段的时候,没有说明是哪个表的id字段,应该加上表名(或者别名)来区分.
- mvc razor中renderPartial,RenderAction,Partial,Action的使用选择
RenderPartial: 通常被用来显示一个功能相对独立的“块”,比如说显示菜单或者导航条. RenderPartial输出的结果被作为调用的View的一部分显示. 这个方法会直接将结果写入到当前 ...
- Servlet/JSP-08 EL表达式
EL - Expression Language 一. 基本语法 EL表达式以 ${} 的形式访问变量的值 1. "." 和 "[]" 运算符 <%@ p ...