c++提供异常机制,这点比C语言灵活且方便
1.引发异常
程序在出现问题的时候将引发异常,使用throw语句跳转并指出异常的相关特征
2.捕获有处理程序的异常
使用异常处理程序捕获异常,异常处理程序位于要处理问题的程序中,
catch(要响应的异常类型)//此类型于throw出的类型一致才能匹配
{
... //相关的处理
}
3.使用try块
标识其中特定的异常可能被激活的代码块,后面可跟多个catch块
try{
.....
throw "123";
}catch(const char* s)
{
cout << s << "被发现" << endl;
exit(0);
}
如果没有catch块能匹配,会默认调用abort()函数,但是可以修改这种行为
异常的类型通常为类类型,
1.这样可以使用不同的异常类型来区分出不同的异常情况
2.类对象可以携带丰富的信息
#include <iostream>
using namespace std;
class bad_hmean
{
private:
int m_a, m_b;
string m_s;
public:
bad_hmean(int a, int b, string s):m_a(a), m_b(b), m_s(s) {};
void msg() {cout << m_s << endl;}
};
double hmean(int a, int b) throw(bad_hmean)//告诉编译器异常规范
{
if(a == -b)
throw bad_hmean(a, b, "err a = -b");
return (double)1 / (double)(a + b);
}
int main()
{
int a, b;
double res;
while(cin >> a >> b)
{
cout << "a=" << a << endl
<< "b=" << b << endl;
try{
res = hmean(a, b);
}catch(bad_hmean &bh)
{
bh.msg();
continue;
}
cout << "res=" << res << endl;
}
return 0;
}
异常跳转将涉及到堆栈解退
程序在throw后将释放堆栈中的内存,一直释放到try块的返回地址(应该是try块的结尾),中间压入堆栈的函数,自动变量,类对象都会被释放,类对象会调用析构函数被析构。
引发异常时编译器总是创建一个临时拷贝,即使catch的是引用。因为发生异常时原有的对象可能已经不存在了。
最好在catch中使用引用:
1.使用引用可以避免拷贝而提高效率。
2.基类的引用可以匹配所有的派生类。(如果有一组通过继承关联起来的异常类型,在异常规范中只用列出一个基类引用就可以了)
如果有一组有派生关系的异常类同时catch,其catch排列顺序于派生顺序相反:
class base1;
class base2:public base;
class base3:public base2;
...
main()
{
try{
...
}
catch(base3 &b2)
{ /*do something*/ }
catch(base2 &b1)
{ /*do something*/ }
catch(base1 &b)//将基类放在最后匹配,使异常捕获有选择的余地
{ /*do something*/ }
catch(...)
{ /*do something*/ }
}
我们使用catch(...)来捕获任何异常,这个语句一般放在最后。
未捕获异常和意外异常
意外异常:带异常规范的函数中引发的异常必须与规范列表中的某种异常匹配,否则为意外异常。
未捕获异常:异常不是在函数中引发的,则必须被捕获,没有被捕获的称未捕获异常
这2种异常默认情况下会导致程序终止。