第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编程与应用程序一样,都有异常处理机制,比如异常的捕获与异常的抛出,本文简单介绍异常捕获与异 ...
随机推荐
- go语言编辑器:liteide
LiteIDE is a simple, open source, cross-platform Go IDE. LiteIDE是一款开源.跨平台的轻量级Go语言集成开发环境(IDE). 项目地址:h ...
- SQLServer表内自关联级联删除
今天处理SQLServer级联删除遇到了很蛋疼的事. SQLServer 不支持表内自关联级联删除,而MySql和Oracle却支持. 貌似原因是SQLServer 会产生循环级联,就不给这样弄.所以 ...
- input输入样式,动画
模板描述:input输入样式 动画,有输入框也有搜索框的样式,多种多样,大家根据自己的喜欢来. 找网站SEO教程,网站模板,以及想要建立个人博客的朋友来涂志海个人博客网,这里有你想要的一切(万一没有的 ...
- Jquery属性获取——attr()与prop()
今天在项目中使用<select></select>下拉菜单时,使用juery操作,使页面加载完菜单默认选中的值为2,我一开始的操作如下: <!--html部分--> ...
- Python将Excel生成SHP
#!/usr/bin/env python # -*- coding: utf-8 -*- import gdal import xlrd import shapefile # open the ex ...
- 如何去定义一个jquery插件
扩展jquery的时候.最核心的方法是以下两种: $.extend(object) 可以理解为jquery添加一个静态方法 $.fn.extend(object) 可以理解为jquery实例添加一个方 ...
- Android draw9patch 图片制作与使用
理解一下4句话: 上边 决定左右拉升不变形 左边 决定上下拉升不变形 右边 设置内容高度区域 下边 设置内容宽度区域 下面我拿张图片分别举例说明: 1.QQ多彩气泡 聊天对话框也用.9图片制作 继承过 ...
- 在IDEA 上使用Git查看主干和分支等
使用IDEA 查看提交历史时,IDEA默认显示的页面是你当前页面的提交历史: 然后,你点击左侧的 log 键,则可以显示整个团队每个人的提交记录: 在选中每一次的记录以后,会弹出每次提交的所有文件(对 ...
- ASP.NET MVC Bootstrap极速开发框架
前言 每次新开发项目都要从头开始设计?有木有一个通用的快速开发框架?并且得是ASP.NET MVC And Bootstrap?数据库不要手工创建?框架对未来业务支持的扩展性好?这么简单的功能还需要 ...
- 小议如何使用APPLY
简介 如果你打算为在结果集中的每条记录写一个调用表值函数或者表值表达式的select语句,那么你就能用到APPLY 操作符来实现.一般又两种形式写法: 第一种格式就是CROSS APPLY.这种格式可 ...