- 异常处理语法
(1)throw语法:
throw 表达式;
(2)try语法:
try
复合语句
catch(异常声明)
复合语句
catch(异常声明)
复合语句
...
2、解析各语句
(1)throw:若某段程序中发现自己不能处理的异常,则可使用throw表达式来抛掷这个异常,将它抛掷给调用者。语法与return类似。
(2)try:try子句后的复合语句是代码的保护段。如果预料某段陈旭有可能发生异常,则将它放在try后。若这段代码运行时真的碰到了异常情况,其中的throw表达式就会抛掷异常。
(3)catch:catch子句后的复合语句是异常处理程序,捕获由throw抛掷的异常。类似是只包含一个形参的函数形参列表,该形参可以是某个类型的值,也可以是引用。
当异常被抛掷后,catch子句便依次被检查,若某个catch子句的异常声明的类型与被抛掷的异常类型一致,则执行该段异常处理程序。若异常类型声明时一个省略号(…),catch子句便处理所有类型的异常,这段处理程序必须是try块的最后一段处理程序。
注:异常声明的形式和函数形参的声明形式类似,catch子句的异常声明可以不给出异常参数名称,只是在这种情况下在复合语句中就无法访问该异常变量。
3、异常处理的执行过程
(1)程序通过正常的顺序执行行到达try语句,然后执行try块内的保护段。
(2)若在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从异常被抛掷的try块后跟随的最后一个catch子句后面的语句继续执行下去。
(3)程序行到一个throw表达式时,一个异常对象会别创建。若异常的抛出点本身在一个try子句内,则该try语句后的catch子句会顺序检查异常类型是否与声明的类型匹配;若异常抛出点本身不在任何try子句内,或抛出的异常与各个catch子句所声明的类型皆不匹配,则结束当前函数的执行,回到当前函数的调用点,把调用点作为异常的抛出点,然后重复这一过程,知道异常成功被一个catch语句捕获。
(4)若始终未找到与被抛出异常匹配的catch子句,最终main函数会结束执行,则运行库函数terminate将自动调用,而函数terminate的默认功能是终止程序。
(5)若找到一个匹配的catch子句,则catch子句后的复合语句会被执行。复合语句执行完毕后当前的try块(含try子句和catch子句)即执行完毕。
4、抛出的异常与一个catch子句中声明的异常匹配的情况
(1)catch子句中声明的异常类型就是抛出异常对象的类型或引用。
(2)catch子句中声明的异常类型是抛出异常对象的类型的公共基类或引用。
(3)抛出的异常类型和catch子句中声明的异常类型皆为指针类型,且前者到后者可以隐含转换。
5、例子
/*处理除零异常*/
#include<iostream>
using namespace std;
int devide(int x, int y) {
if (y == 0) {
throw x;
}
return x / y;
}
int main()
{
try {
cout << "5/2=" << devide(5, 2) << endl;
cout << "5/0=" << devide(5, 0) << endl;
}
catch (int e) {
cout << e << "is divided by zero!" << endl;
}
catch (...) {
cout << "unknow err!" << endl;
}
system("pause");
return 0;
}
6、重新抛出
有时一个单独的catch语句不能完整的处理某个异常,在执行某些校正操作之后,当前catch可能会决定由调用链更上一层的函数接着处理。一条catch语句通过重新抛出的操作将异常传递给另一个catch。重新抛出仍然是一条throw语句,只是不包含任何表达式,即throw;
很多时候,catch语句会改变其参数的内容。若在改变了参数的内容后catch语句重新抛出异常,则只有当catch异常声明是引用类型时我们队参数所做的改变才会被保留并继续传播。
例:
catch (my_error &eObj){ //引用类型
eObj.status = errCodes::seceveErr; //修改了异常对象
throw; //异常对象的status常用是severeErr
}catch(other_error eObj){ //非引用类型
eObj.status = errorCodes::badErr; //只修改了异常对象的局部副本
throw; //遗产对象的status常用没有改变
}
7、异常接口声明
为了加强程序的可读性,使函数的用户能够方便的知道所使用的函数会抛掷哪些异常,可在函数的声明中列出这个函数可能抛掷的所有异常类型。
void fun() throw(A, B, C, D); //该函数能够且只能够抛掷类型A, B, C, D及其子类型的异常
void fun() throw(); //不抛掷任何异常的声明
8、异常处理的构造和析构
(1)C++异常处理的真正功能,不仅在于它能够处理各种不同类型的异常,还在于它具有为异常抛掷前构造的所有局部对象自动调用析构函数的能力。
异常别抛出后,从进入try块起到异常别抛掷前,这期间在栈上构造的所有对象都会被自动析构,析构的顺序与构造的顺序相反。这一过程称为栈的解旋。
例;
#include<iostream>
#include<string>
using namespace std;
class MyException
{
public:
MyException(const string &message) :message(message) {}
~MyException(){}
const string& getMessage() const {
return message;
}
private:
string message;
};
class Demo {
public:
Demo() {
cout << "Demo" << endl;
}
~Demo() {
cout << "Destroy Demo" << endl;
}
};
void func()throw(MyException) {
Demo d;
cout << "Throw exception in func" << endl;
throw MyException("exception throw by fun");
}
int main()
{
try {
func();
}
catch (MyException& e) {
cout << "caught an exceotion: " << e.getMessage() << endl;
}
system("pause");
return 0;
}
运行结果:
(2)函数try语句块与构造函数
函数try语句块使得一组catch语句既能处理构造函数体(或析构函数体),也能处理构造函数的初始化构成(或析构函数的初始化过程)。
例:
template<typename T>
Blob<T>::Blob(std::initializer_list<T> il) try:
data(std::make_shared<std::vector<T>>(il)){
//空函数体
}catch(const std::bad__alloc &e){
hadle_out_of_memory(e);
}
9、标准程序库异常处理
C++标准提供一组标准异常类,这些类以基类Exception开始。该基类仅仅定义了拷贝函数、拷贝赋值运算符、一个虚析构函数,和一个成员函数what(),用于返回错误信息(返回类型为const char *)。声明如下
virtual const char * what() const throw();
10、noexcept异常说明
c++11中noexcept说明指定某个函数不会抛出异常
例:
void fun(int) noexcept; //不会抛出异常
void fun(int); //可能抛出异常