异常的概念:
是当一个函数发现自己无法处理的错误时抛出异常,让函数的调用者直接或者间接的处理这个问题。
关于异常的引入程序:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Exception
{
public:
Exception(int id, const char* msg)
:_errid(id)
,_errmsg(msg)
{}
const char* What()
{
return _errmsg.c_str();
}
protected:
int _errid;//错误码
string _errmsg;//错误信息
};
//假设F2出现了一中错误
void F2()
{
char* p1 = (char*)malloc(10000);
if (p1 == 0)//如果等于零说明开辟空间失败
{
string msg;
throw msg;//抛出异常
}
FILE*fout = fopen("text.txt", "r");
if (fout == NULL)
{
throw Exception(1, "打开文件失败");
}
cout << "F2" << endl;
}
void F1()
{
F2();
cout << "F1" << endl;
}
int main()
{
try
{
F1();
}
catch (string msg)//捕获
{
cout << msg << endl;
}
catch (Exception e)
{
cout << e.What() << endl;
}
return 0;
}
分析上面的程序:
抛出异常的时候会暂停当前函数的执行,并且查找对应匹配的catch语句。我们看到F2先抛出异常,那么就先检查F2是否存在try,如果有则查找匹配的catch语句,则处理,没有就退出F2当前函数的栈,在F1中继续查找,重复F2的步骤,如果在F1中没有找到,就到达main函数的栈进行查找,如果还是没有找到,那么程序终止。
异常的抛出和捕获:
异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个处理代码。
被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
抛出特殊异常后会释放局部存储对象,所以被抛出的对象也就还给系统了,throw表达式会初始化一个抛除特殊的异常对象副本,异常对象由编译管理,异常对象在传给对应的catch处理之后撤销。
异常对象的类型与catch说明符的类型一般要完全匹配,除了三种特殊情况:
1,从非const对象到const的转换
2,允许从派生类到基类类型的转换
3,将数组转换为指针数组类型的指针,将函数转换为指向函数类型的指针
举一个关于异常的实际应用:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Exception
{
public:
Exception(int id, const char* msg)
:_errid(id)
, _errmsg(msg)
{}
virtual const char* What()=0;//不实例化对象是因为没有意义,具体由子类完成
int GetErrid()
{
return _errid;
}
protected:
int _errid;
string _errmsg;
};
class OutOfRang:public Exception
{
public:
OutOfRang(const char* msg)
:Exception(1, msg)
{}
virtual const char* What()
{
_errmsg += "越界";
return _errmsg.c_str();
}
};
class BadAlloc :public Exception
{
public:
BadAlloc(const char* msg)
:Exception(1, msg)
{}
virtual const char* What()
{
_errmsg += "申请内存失败";
return _errmsg.c_str();
}
};
template<class T,size_t N=100>
class Array
{
public:
T& operator[](size_t index)
{
if (index >= N)//越界
{
throw OutOfRang(__FUNCTION__);
}
return _a[index];
}
protected:
T _a[N];
};
int main()
{
try
{
Array<int, 10>a1;
a1[0] = 10;
a1[10] = 10;
}
catch (Exception& e)
{
cout << e.What() << endl;
}
return 0;
}
异常与错误码相比有什么优缺点:
优点:异常能够更清晰的反馈错误信息;
许多c++的第三方库使用异常。
缺点:异常会导致程序控制流通过查看代码无法确定:函数有可能在不确定的地方返回,从而导致代码管理和调试困难;
异常安全需要RAII和不同编码实践。